1 //---------------------------------------------------------------------- 2 // This software is part of the OpenBeOS distribution and is covered 3 // by the OpenBeOS license. 4 //--------------------------------------------------------------------- 5 /*! 6 \file Pattern.cpp 7 MIME sniffer pattern implementation 8 */ 9 10 #include <sniffer/Err.h> 11 #include <sniffer/Pattern.h> 12 #include <DataIO.h> 13 #include <stdio.h> // for SEEK_* defines 14 #include <new> 15 16 using namespace BPrivate::Storage::Sniffer; 17 18 Pattern::Pattern(const std::string &string, const std::string &mask) 19 : fCStatus(B_NO_INIT) 20 , fErrorMessage(NULL) 21 { 22 SetTo(string, mask); 23 } 24 25 Pattern::Pattern(const std::string &string) 26 : fCStatus(B_NO_INIT) 27 , fErrorMessage(NULL) 28 { 29 // Build a mask with all bits turned on of the 30 // appropriate length 31 std::string mask = ""; 32 for (uint i = 0; i < string.length(); i++) 33 mask += (char)0xFF; 34 SetTo(string, mask); 35 } 36 37 Pattern::~Pattern() { 38 delete fErrorMessage; 39 } 40 41 status_t 42 Pattern::InitCheck() const { 43 return fCStatus; 44 } 45 46 Err* 47 Pattern::GetErr() const { 48 if (fCStatus == B_OK) 49 return NULL; 50 else 51 return new(std::nothrow) Err(*fErrorMessage); 52 } 53 54 void dumpStr(const std::string &string, const char *label = NULL) { 55 if (label) 56 printf("%s: ", label); 57 for (uint i = 0; i < string.length(); i++) 58 printf("%x ", string[i]); 59 printf("\n"); 60 } 61 62 status_t 63 Pattern::SetTo(const std::string &string, const std::string &mask) { 64 fString = string; 65 if (fString.length() == 0) { 66 SetStatus(B_BAD_VALUE, "Sniffer pattern error: illegal empty pattern"); 67 } else { 68 fMask = mask; 69 // dumpStr(string, "data"); 70 // dumpStr(mask, "mask"); 71 if (fString.length() != fMask.length()) { 72 SetStatus(B_BAD_VALUE, "Sniffer pattern error: pattern and mask lengths do not match"); 73 } else { 74 SetStatus(B_OK); 75 } 76 } 77 } 78 79 /*! \brief Looks for a pattern match in the given data stream, starting from 80 each offset withing the given range. Returns true is a match is found, 81 false if not. 82 */ 83 bool 84 Pattern::Sniff(Range range, BPositionIO *data, bool caseInsensitive) const { 85 int32 start = range.Start(); 86 int32 end = range.End(); 87 off_t size = data->Seek(0, SEEK_END); 88 if (end >= size) 89 end = size-1; // Don't bother searching beyond the end of the stream 90 for (int i = start; i <= end; i++) { 91 if (Sniff(i, size, data, caseInsensitive)) 92 return true; 93 } 94 return false; 95 } 96 97 // BytesNeeded 98 /*! \brief Returns the number of bytes needed to perform a complete sniff, or an error 99 code if something goes wrong. 100 */ 101 ssize_t 102 Pattern::BytesNeeded() const 103 { 104 ssize_t result = InitCheck(); 105 if (result == B_OK) 106 result = fString.length(); 107 return result; 108 } 109 110 //#define OPTIMIZATION_IS_FOR_CHUMPS 111 #if OPTIMIZATION_IS_FOR_CHUMPS 112 bool 113 Pattern::Sniff(off_t start, off_t size, BPositionIO *data, bool caseInsensitive) const { 114 off_t len = fString.length(); 115 char *buffer = new(nothrow) char[len+1]; 116 if (buffer) { 117 ssize_t bytesRead = data->ReadAt(start, buffer, len); 118 // \todo If there are fewer bytes left in the data stream 119 // from the given position than the length of our data 120 // string, should we just return false (which is what we're 121 // doing now), or should we compare as many bytes as we 122 // can and return true if those match? 123 if (bytesRead < len) 124 return false; 125 else { 126 bool result = true; 127 if (caseInsensitive) { 128 for (int i = 0; i < len; i++) { 129 char secondChar; 130 if ('A' <= fString[i] && fString[i] <= 'Z') 131 secondChar = 'a' + (fString[i] - 'A'); // Also check lowercase 132 else if ('a' <= fString[i] && fString[i] <= 'z') 133 secondChar = 'A' + (fString[i] - 'a'); // Also check uppercase 134 else 135 secondChar = fString[i]; // Check the same char twice as punishment for doing a case insensitive search ;-) 136 if (((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) 137 && ((secondChar & fMask[i]) != (buffer[i] & fMask[i]))) 138 { 139 result = false; 140 break; 141 } 142 } 143 } else { 144 for (int i = 0; i < len; i++) { 145 if ((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) { 146 result = false; 147 break; 148 } 149 } 150 } 151 return result; 152 } 153 } else 154 return false; 155 } 156 #else 157 bool 158 Pattern::Sniff(off_t start, off_t size, BPositionIO *data, bool caseInsensitive) const { 159 off_t len = fString.length(); 160 char *buffer = new(std::nothrow) char[len+1]; 161 if (buffer) { 162 ssize_t bytesRead = data->ReadAt(start, buffer, len); 163 // \todo If there are fewer bytes left in the data stream 164 // from the given position than the length of our data 165 // string, should we just return false (which is what we're 166 // doing now), or should we compare as many bytes as we 167 // can and return true if those match? 168 if (bytesRead < len) 169 return false; 170 else { 171 bool result = true; 172 if (caseInsensitive) { 173 for (int i = 0; i < len; i++) { 174 char secondChar; 175 if ('A' <= fString[i] && fString[i] <= 'Z') 176 secondChar = 'a' + (fString[i] - 'A'); // Also check lowercase 177 else if ('a' <= fString[i] && fString[i] <= 'z') 178 secondChar = 'A' + (fString[i] - 'a'); // Also check uppercase 179 else 180 secondChar = fString[i]; // Check the same char twice as punishment for doing a case insensitive search ;-) 181 if (((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) 182 && ((secondChar & fMask[i]) != (buffer[i] & fMask[i]))) 183 { 184 result = false; 185 break; 186 } 187 } 188 } else { 189 for (int i = 0; i < len; i++) { 190 if ((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) { 191 result = false; 192 break; 193 } 194 } 195 } 196 return result; 197 } 198 } else 199 return false; 200 } 201 #endif 202 203 void 204 Pattern::SetStatus(status_t status, const char *msg) { 205 fCStatus = status; 206 if (status == B_OK) 207 SetErrorMessage(NULL); 208 else { 209 if (msg) 210 SetErrorMessage(msg); 211 else { 212 SetErrorMessage("Sniffer parser error: Pattern::SetStatus() -- NULL msg with non-B_OK status.\n" 213 "(This is officially the most helpful error message you will ever receive ;-)"); 214 } 215 } 216 } 217 218 void 219 Pattern::SetErrorMessage(const char *msg) { 220 delete fErrorMessage; 221 fErrorMessage = (msg) ? (new(std::nothrow) Err(msg, -1)) : (NULL); 222 } 223 224 225 226