xref: /haiku/src/kits/storage/sniffer/Pattern.cpp (revision 95c9effd68127df2dce202d5e254a7c86560010a)
1 //----------------------------------------------------------------------
2 //  This software is part of the OpenBeOS distribution and is covered
3 //  by the MIT 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