xref: /haiku/src/kits/storage/sniffer/Pattern.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
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