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 return fCStatus; 78 } 79 80 /*! \brief Looks for a pattern match in the given data stream, starting from 81 each offset withing the given range. Returns true is a match is found, 82 false if not. 83 */ 84 bool 85 Pattern::Sniff(Range range, BPositionIO *data, bool caseInsensitive) const { 86 int32 start = range.Start(); 87 int32 end = range.End(); 88 off_t size = data->Seek(0, SEEK_END); 89 if (end >= size) 90 end = size-1; // Don't bother searching beyond the end of the stream 91 for (int i = start; i <= end; i++) { 92 if (Sniff(i, size, data, caseInsensitive)) 93 return true; 94 } 95 return false; 96 } 97 98 // BytesNeeded 99 /*! \brief Returns the number of bytes needed to perform a complete sniff, or an error 100 code if something goes wrong. 101 */ 102 ssize_t 103 Pattern::BytesNeeded() const 104 { 105 ssize_t result = InitCheck(); 106 if (result == B_OK) 107 result = fString.length(); 108 return result; 109 } 110 111 //#define OPTIMIZATION_IS_FOR_CHUMPS 112 #if OPTIMIZATION_IS_FOR_CHUMPS 113 bool 114 Pattern::Sniff(off_t start, off_t size, BPositionIO *data, bool caseInsensitive) const { 115 off_t len = fString.length(); 116 char *buffer = new(nothrow) char[len+1]; 117 if (buffer) { 118 ssize_t bytesRead = data->ReadAt(start, buffer, len); 119 // \todo If there are fewer bytes left in the data stream 120 // from the given position than the length of our data 121 // string, should we just return false (which is what we're 122 // doing now), or should we compare as many bytes as we 123 // can and return true if those match? 124 if (bytesRead < len) 125 return false; 126 else { 127 bool result = true; 128 if (caseInsensitive) { 129 for (int i = 0; i < len; i++) { 130 char secondChar; 131 if ('A' <= fString[i] && fString[i] <= 'Z') 132 secondChar = 'a' + (fString[i] - 'A'); // Also check lowercase 133 else if ('a' <= fString[i] && fString[i] <= 'z') 134 secondChar = 'A' + (fString[i] - 'a'); // Also check uppercase 135 else 136 secondChar = fString[i]; // Check the same char twice as punishment for doing a case insensitive search ;-) 137 if (((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) 138 && ((secondChar & fMask[i]) != (buffer[i] & fMask[i]))) 139 { 140 result = false; 141 break; 142 } 143 } 144 } else { 145 for (int i = 0; i < len; i++) { 146 if ((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) { 147 result = false; 148 break; 149 } 150 } 151 } 152 return result; 153 } 154 } else 155 return false; 156 } 157 #else 158 bool 159 Pattern::Sniff(off_t start, off_t size, BPositionIO *data, bool caseInsensitive) const { 160 off_t len = fString.length(); 161 char *buffer = new(std::nothrow) char[len+1]; 162 if (buffer) { 163 ssize_t bytesRead = data->ReadAt(start, buffer, len); 164 // \todo If there are fewer bytes left in the data stream 165 // from the given position than the length of our data 166 // string, should we just return false (which is what we're 167 // doing now), or should we compare as many bytes as we 168 // can and return true if those match? 169 if (bytesRead < len) 170 return false; 171 else { 172 bool result = true; 173 if (caseInsensitive) { 174 for (int i = 0; i < len; i++) { 175 char secondChar; 176 if ('A' <= fString[i] && fString[i] <= 'Z') 177 secondChar = 'a' + (fString[i] - 'A'); // Also check lowercase 178 else if ('a' <= fString[i] && fString[i] <= 'z') 179 secondChar = 'A' + (fString[i] - 'a'); // Also check uppercase 180 else 181 secondChar = fString[i]; // Check the same char twice as punishment for doing a case insensitive search ;-) 182 if (((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) 183 && ((secondChar & fMask[i]) != (buffer[i] & fMask[i]))) 184 { 185 result = false; 186 break; 187 } 188 } 189 } else { 190 for (int i = 0; i < len; i++) { 191 if ((fString[i] & fMask[i]) != (buffer[i] & fMask[i])) { 192 result = false; 193 break; 194 } 195 } 196 } 197 return result; 198 } 199 } else 200 return false; 201 } 202 #endif 203 204 void 205 Pattern::SetStatus(status_t status, const char *msg) { 206 fCStatus = status; 207 if (status == B_OK) 208 SetErrorMessage(NULL); 209 else { 210 if (msg) 211 SetErrorMessage(msg); 212 else { 213 SetErrorMessage("Sniffer parser error: Pattern::SetStatus() -- NULL msg with non-B_OK status.\n" 214 "(This is officially the most helpful error message you will ever receive ;-)"); 215 } 216 } 217 } 218 219 void 220 Pattern::SetErrorMessage(const char *msg) { 221 delete fErrorMessage; 222 fErrorMessage = (msg) ? (new(std::nothrow) Err(msg, -1)) : (NULL); 223 } 224 225 226 227