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