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