GCC Code Coverage Report


Directory: ./
File: common.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 550 690 79.7%
Functions: 46 56 82.1%
Branches: 394 813 48.5%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16
17 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
20
21 #if defined(__APPLE__)
22 #include <copyfile.h>
23 #else
24 #include <sys/sendfile.h>
25 #endif
26
27 #include <arpa/inet.h>
28 #include <curl/curl.h>
29 #include <net/if.h>
30 #include <netinet/in.h>
31 #include <sys/file.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include <atomic>
37 #include <cstdint>
38 #include <ctime>
39 #include <ctype.h>
40 #include <cxxabi.h>
41 #include <dirent.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <fstream>
45 #include <ifaddrs.h>
46 #include <iomanip>
47 #include <iterator>
48 #include <list>
49 #include <map>
50 #include <mutex>
51 #include <netdb.h>
52 #include <pwd.h>
53 #include <sstream>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <string>
58 #include <unistd.h>
59 #include <utility>
60 #include <vector>
61
62 #include "common.h"
63 #include "CurlSocket.h"
64 #include "fppversion.h"
65 #include "log.h"
66
67 /*
68 * Dump memory block in hex and human-readable formats
69 */
70 3061 void HexDump(const char* title, const void* data, int len, FPPLoggerInstance& facility, int perLine) {
71 3061 int l = 0;
72 3061 int i = 0;
73 3061 int x = 0;
74 3061 int y = 0;
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3061 times.
3061 if (perLine <= 0) {
76 ✗ perLine = 16;
77 }
78 3061 unsigned char* ch = (unsigned char*)data;
79 3061 unsigned char* str = new unsigned char[perLine + 1];
80
81 3061 int maxLen = perLine * 7 + 20;
82 3061 char* tmpStr = new char[maxLen];
83 3061 tmpStr[0] = 0;
84
85 // The title is data, not a format string.
86
2/4
✓ Branch 0 taken 3061 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3061 times.
✗ Branch 3 not taken.
3061 if (title && strlen(title)) {
87 3061 snprintf(tmpStr, maxLen, "%s: (%d bytes)\n", title, len);
88 3061 LogInfo(facility, "%s", tmpStr);
89 }
90 // Nothing to dump. The tail below appends to tmpStr, which was never
91 // started with an offset (left uninitialized with no title) and would
92 // be logged again.
93
2/4
✓ Branch 0 taken 3061 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3061 times.
3061 if (len <= 0 || !data) {
94 ✗ delete[] tmpStr;
95 ✗ delete[] str;
96 ✗ return;
97 }
98
99
2/2
✓ Branch 0 taken 26347 times.
✓ Branch 1 taken 3061 times.
29408 while (l < len) {
100
2/2
✓ Branch 0 taken 3061 times.
✓ Branch 1 taken 23286 times.
26347 if (x == 0) {
101 3061 snprintf(tmpStr, maxLen, "%06x: ", i);
102 }
103
104
2/2
✓ Branch 0 taken 26241 times.
✓ Branch 1 taken 106 times.
26347 if (x < perLine) {
105 26241 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), "%02x ", *ch & 0xFF);
106 26241 str[x] = *ch;
107 26241 x++;
108 26241 i++;
109 } else {
110 106 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), " ");
111
2/2
✓ Branch 0 taken 1696 times.
✓ Branch 1 taken 106 times.
1802 for (; x > 0; x--) {
112
2/4
✓ Branch 0 taken 1696 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1696 times.
1696 if (str[perLine - x] == '%' || str[perLine - x] == '\\') {
113 // these are escapes for the Log call, so don't display them
114 ✗ snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), ".");
115
4/4
✓ Branch 0 taken 1593 times.
✓ Branch 1 taken 103 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1584 times.
1696 } else if (isgraph(str[perLine - x]) || str[perLine - x] == ' ') {
116 112 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), "%c", str[perLine - x]);
117 } else {
118 1584 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), ".");
119 }
120 }
121
122 106 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), "\n");
123 106 LogInfo(facility, "%s", tmpStr);
124 106 x = 0;
125
126 106 snprintf(tmpStr, maxLen, "%06x: ", i);
127 106 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), "%02x ", *ch & 0xFF);
128 106 str[x] = *ch;
129 106 x++;
130 106 i++;
131 }
132
133 26347 l++;
134 26347 ch++;
135 }
136
2/2
✓ Branch 0 taken 24325 times.
✓ Branch 1 taken 3061 times.
27386 for (y = x; y < perLine; y++) {
137 24325 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), " ");
138 }
139 3061 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), " ");
140
2/2
✓ Branch 0 taken 24651 times.
✓ Branch 1 taken 3061 times.
27712 for (y = 0; y < x; y++) {
141
4/4
✓ Branch 0 taken 24579 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 74 times.
✓ Branch 3 taken 24505 times.
24651 if (str[y] == '%' || str[y] == '\\') {
142 // these are escapes for the Log call, so don't display them
143 146 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), ".");
144
4/4
✓ Branch 0 taken 17707 times.
✓ Branch 1 taken 6798 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 17635 times.
24505 } else if (isgraph(str[y]) || str[y] == ' ') {
145 6870 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), "%c", str[y]);
146 } else {
147 17635 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), ".");
148 }
149 }
150
151 3061 snprintf(tmpStr + strlen(tmpStr), maxLen - strlen(tmpStr), "\n");
152 3061 LogInfo(facility, "%s", tmpStr);
153
1/2
✓ Branch 0 taken 3061 times.
✗ Branch 1 not taken.
3061 delete[] tmpStr;
154
1/2
✓ Branch 0 taken 3061 times.
✗ Branch 1 not taken.
3061 delete[] str;
155 }
156
157 76 int CheckForHostSpecificFile(const std::string& hostname, std::string& filename) {
158
1/2
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
76 std::string localFilename = filename;
159
160 76 int len = (int)localFilename.length();
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (len < 4)
162 ✗ return 0;
163 76 int extIdx = 0;
164
165 // Check for 3 or 4-digit extension
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if (localFilename[len - 4] == '.') {
167 ✗ extIdx = len - 4;
168
5/6
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 67 times.
✓ Branch 5 taken 9 times.
76 } else if (len >= 5 && localFilename[len - 5] == '.') {
169 67 extIdx = len - 5;
170 }
171
172
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 9 times.
76 if (extIdx) {
173 // Preserve the extension including the dot
174
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 std::string ext = localFilename.substr(extIdx);
175
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 localFilename = localFilename.substr(0, extIdx);
176
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 localFilename += "-";
177
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 localFilename += hostname;
178
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 localFilename += ext;
179
180
2/4
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 67 times.
67 if (FileExists(localFilename)) {
181 ✗ LogDebug(VB_SEQUENCE, "Found %s to use instead of %s\n",
182 localFilename.c_str(), filename.c_str());
183 ✗ filename = localFilename;
184 ✗ return 1;
185 } else {
186 // Replace hyphen with an underscore and recheck
187 67 localFilename[extIdx] = '_';
188
2/4
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 67 times.
67 if (FileExists(localFilename)) {
189 ✗ LogDebug(VB_SEQUENCE, "Found %s to use instead of %s\n",
190 localFilename.c_str(), filename.c_str());
191 ✗ filename = localFilename;
192 ✗ return 1;
193 }
194 }
195
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 }
196
197 76 return 0;
198 76 }
199 39 int CheckForHostSpecificFile(const char* hostname, char* filename) {
200
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
78 std::string f = filename;
201
3/6
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 39 times.
78 if (CheckForHostSpecificFile(hostname, f)) {
202 // In-tree callers pass a 2048-byte tmpFilename (Sequence.cpp). Clamp to
203 // that size to avoid overflowing a smaller plugin-provided buffer while
204 // preserving the original strcpy semantics for fitting names. Truncation
205 // is treated as "no host file" so the caller keeps the original name.
206 ✗ constexpr size_t kSafeLimit = 2048;
207 ✗ if (f.size() >= kSafeLimit) {
208 ✗ LogErr(VB_SEQUENCE, "Host-specific filename too long (%zu >= %zu), keeping %s\n",
209 f.size(), kSafeLimit, filename);
210 ✗ return 0;
211 }
212 ✗ snprintf(filename, kSafeLimit, "%s", f.c_str());
213 ✗ return 1;
214 }
215 39 return 0;
216 39 }
217 ✗ char* FindInterfaceForIP(char* ip) {
218 struct ifaddrs *ifaddr, *ifa;
219 int family, s, n;
220 char host[NI_MAXHOST];
221 char interfaceIP[16];
222 // Per thread, and cleared on every call: a lookup that found nothing
223 // returned the previous call's interface.
224 static thread_local char interface[IFNAMSIZ] = "";
225 ✗ interface[0] = 0;
226
227 ✗ if (getifaddrs(&ifaddr) == -1) {
228 ✗ LogErr(VB_SETTING, "Error getting interfaces list: %s\n",
229 FPPstrerror(errno));
230 ✗ return interface;
231 }
232
233 ✗ for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
234 ✗ if (ifa->ifa_addr == NULL)
235 ✗ continue;
236
237 ✗ if (ifa->ifa_addr->sa_family == AF_INET) {
238 ✗ s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in),
239 host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
240 ✗ if (s != 0) {
241 ✗ LogErr(VB_SETTING, "getnameinfo failed");
242 ✗ freeifaddrs(ifaddr);
243 ✗ return interface;
244 }
245
246 ✗ if (!strcmp(host, ip)) {
247 ✗ snprintf(interface, sizeof(interface), "%s", ifa->ifa_name);
248 ✗ freeifaddrs(ifaddr);
249 ✗ return interface;
250 }
251 }
252 }
253
254 ✗ freeifaddrs(ifaddr);
255 ✗ return interface;
256 }
257
258 3726 static void ReplaceString(std::string& str, std::string pattern, std::string replacement) {
259 3726 std::size_t found = str.find(pattern);
260
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3726 times.
3734 while (found != std::string::npos) {
261 8 str.replace(found, pattern.length(), replacement);
262
263 // Resume after what was inserted, not from the start: a value that
264 // contains its own keyword (a playlist named "%PLAYLIST_NAME%") was
265 // found again inside the replacement, forever, hanging whichever
266 // thread fired the preset.
267 8 found = str.find(pattern, found + replacement.length());
268 }
269 3726 }
270 666 std::string ReplaceKeywords(std::string str, std::map<std::string, std::string>& keywords) {
271 666 std::string key;
272
273
2/2
✓ Branch 0 taken 396 times.
✓ Branch 1 taken 666 times.
1062 for (auto& k : keywords) {
274
1/2
✓ Branch 0 taken 396 times.
✗ Branch 1 not taken.
396 key = std::string("%");
275
1/2
✓ Branch 0 taken 396 times.
✗ Branch 1 not taken.
396 key += k.first;
276
2/4
✓ Branch 0 taken 396 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 396 times.
✗ Branch 3 not taken.
396 key += std::string("%");
277
278
3/6
✓ Branch 0 taken 396 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 396 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 396 times.
✗ Branch 5 not taken.
396 ReplaceString(str, key, k.second);
279 }
280
281 666 std::time_t currTime = std::time(NULL);
282 struct tm now;
283 666 localtime_r(&currTime, &now);
284 char tmpStr[20];
285
286 666 snprintf(tmpStr, sizeof(tmpStr), "%02d:%02d:%02d", now.tm_hour, now.tm_min, now.tm_sec);
287
3/6
✓ Branch 0 taken 666 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 666 times.
✗ Branch 5 not taken.
1998 ReplaceString(str, "%TIME%", tmpStr);
288
289 666 snprintf(tmpStr, sizeof(tmpStr), "%04d-%02d-%02d", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
290
3/6
✓ Branch 0 taken 666 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 666 times.
✗ Branch 5 not taken.
2664 ReplaceString(str, "%DATE%", tmpStr);
291
292
4/8
✓ Branch 0 taken 666 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 666 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 666 times.
✗ Branch 7 not taken.
2664 ReplaceString(str, "%FPP_VERSION%", getFPPVersionTriplet());
293
4/8
✓ Branch 0 taken 666 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 666 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 666 times.
✗ Branch 7 not taken.
2664 ReplaceString(str, "%FPP_SOURCE_VERSION%", getFPPVersion());
294
4/8
✓ Branch 0 taken 666 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 666 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 666 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 666 times.
✗ Branch 7 not taken.
1998 ReplaceString(str, "%FPP_BRANCH%", getFPPBranch());
295
296 1332 return str;
297 666 }
298
299 /////////////////////////////////////////////////////////////////////////////
300 /*
301 * Merge the contens of Json::Value b into Json::Value a
302 */
303 ✗ void MergeJsonValues(Json::Value& a, Json::Value& b) {
304 ✗ if (!a.isObject() || !b.isObject())
305 ✗ return;
306
307 ✗ Json::Value::Members memberNames = b.getMemberNames();
308
309 ✗ for (unsigned int i = 0; i < memberNames.size(); i++) {
310 ✗ std::string key = memberNames[i];
311
312 ✗ if ((a[key].type() == Json::objectValue) &&
313 ✗ (b[key].type() == Json::objectValue)) {
314 ✗ MergeJsonValues(a[key], b[key]);
315 } else {
316 ✗ a[key] = b[key];
317 }
318 ✗ }
319 ✗ }
320
321 /*
322 *
323 */
324 ✗ Json::Value LoadJsonFromFile(const std::string& filename) {
325 ✗ Json::Value root;
326
327 ✗ LoadJsonFromFile(filename, root);
328
329 ✗ return root;
330 ✗ }
331
332 /*
333 *
334 */
335 190067 Json::Value LoadJsonFromString(const std::string& str) {
336 190067 Json::Value root;
337
2/2
✓ Branch 0 taken 190065 times.
✓ Branch 1 taken 2 times.
190067 bool result = LoadJsonFromString(str, root);
338
339 190065 return root;
340 2 }
341
342 /*
343 *
344 */
345 429000 bool LoadJsonFromString(const std::string& str, Json::Value& root) {
346
1/2
✓ Branch 0 taken 429000 times.
✗ Branch 1 not taken.
429000 Json::CharReaderBuilder builder;
347
1/2
✓ Branch 0 taken 429000 times.
✗ Branch 1 not taken.
429000 Json::CharReader* reader = builder.newCharReader();
348 429000 std::string errors;
349
350
3/6
✓ Branch 0 taken 429000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 429000 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 429000 times.
✗ Branch 5 not taken.
1287000 builder["collectComments"] = false;
351
352
2/2
✓ Branch 0 taken 428899 times.
✓ Branch 1 taken 101 times.
429000 bool success = reader->parse(str.c_str(), str.c_str() + str.size(), &root, &errors);
353
1/2
✓ Branch 0 taken 428899 times.
✗ Branch 1 not taken.
428899 delete reader;
354
355
2/2
✓ Branch 0 taken 462 times.
✓ Branch 1 taken 428437 times.
428899 if (!success) {
356 // Bodies parsed here include HTTP requests and config files that carry
357 // credentials; log only the start, which is enough to see what it was.
358
2/4
✓ Branch 0 taken 462 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 462 times.
✗ Branch 3 not taken.
462 LogErr(VB_GENERAL, "Error parsing JSON string in LoadJsonFromString(): '%s'\n", TruncateForLog(str).c_str());
359
1/2
✓ Branch 0 taken 462 times.
✗ Branch 1 not taken.
462 Json::Value empty;
360
1/2
✓ Branch 0 taken 462 times.
✗ Branch 1 not taken.
462 root = empty;
361 462 return false;
362 462 }
363
364
2/4
✓ Branch 0 taken 428437 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 428437 times.
✗ Branch 3 not taken.
428437 LogDebug(VB_GENERAL, "LoadJsonFromString() loaded: '%s'\n", TruncateForLog(str).c_str());
365
366 428437 return true;
367 429101 }
368
369 19072 bool LoadJsonFromFile(const std::string& filename, Json::Value& root) {
370
3/4
✓ Branch 0 taken 19072 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9189 times.
✓ Branch 3 taken 9883 times.
19072 if (!FileExists(filename)) {
371
1/2
✓ Branch 0 taken 9189 times.
✗ Branch 1 not taken.
9189 LogErr(VB_GENERAL, "JSON File %s does not exist\n", filename.c_str());
372 9189 return false;
373 }
374
375
1/2
✓ Branch 0 taken 9883 times.
✗ Branch 1 not taken.
9883 std::string jsonStr = GetFileContents(filename);
376
377
1/2
✓ Branch 0 taken 9883 times.
✗ Branch 1 not taken.
9883 return LoadJsonFromString(jsonStr, root);
378 9883 }
379
380 ✗ bool LoadJsonFromFile(const char* filename, Json::Value& root) {
381 ✗ std::string filenameStr = filename;
382
383 ✗ return LoadJsonFromFile(filenameStr, root);
384 ✗ }
385
386 184 static const char* JsonTypeName(Json::ValueType t) {
387
4/9
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 59 times.
✓ Branch 7 taken 92 times.
✗ Branch 8 not taken.
184 switch (t) {
388 ✗ case Json::nullValue:
389 ✗ return "null";
390 17 case Json::intValue:
391 17 return "int";
392 ✗ case Json::uintValue:
393 ✗ return "uint";
394 ✗ case Json::realValue:
395 ✗ return "real";
396 16 case Json::stringValue:
397 16 return "string";
398 ✗ case Json::booleanValue:
399 ✗ return "boolean";
400 59 case Json::arrayValue:
401 59 return "array";
402 92 case Json::objectValue:
403 92 return "object";
404 }
405 ✗ return "unknown";
406 }
407
408 19051 bool LoadJsonFromFile(const std::string& filename, Json::Value& root, JsonRoot expected) {
409
2/2
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 18839 times.
19051 Json::ValueType expectedType = (expected == JsonRoot::Array) ? Json::arrayValue : Json::objectValue;
410
411
2/2
✓ Branch 0 taken 9272 times.
✓ Branch 1 taken 9779 times.
19051 if (!LoadJsonFromFile(filename, root)) {
412
1/2
✓ Branch 0 taken 9272 times.
✗ Branch 1 not taken.
9272 root = Json::Value(expectedType);
413 9272 return false;
414 }
415
416 // A null root is not a mismatch. jsoncpp treats null as an empty value of
417 // whatever type it is first used as and never throws on it, and FPP writes
418 // it itself - Variables::save() stores a default-constructed Json::Value,
419 // so a stock variables.json with nothing persisted is literally "null".
420 // Normalize it and report success, exactly as before this check existed.
421
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 9762 times.
9779 if (root.isNull()) {
422
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 root = Json::Value(expectedType);
423 17 return true;
424 }
425
426
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 9737 times.
9762 if (root.type() != expectedType) {
427 25 LogErr(VB_GENERAL, "JSON File %s has a %s at its root, expected %s - treating as empty\n",
428 filename.c_str(), JsonTypeName(root.type()), JsonTypeName(expectedType));
429
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 root = Json::Value(expectedType);
430 25 return false;
431 }
432
433 9737 return true;
434 }
435
436 3916 Json::Value LoadJsonFromFile(const std::string& filename, JsonRoot expected) {
437 3916 Json::Value root;
438
439
1/2
✓ Branch 0 taken 3916 times.
✗ Branch 1 not taken.
3916 LoadJsonFromFile(filename, root, expected);
440
441 3916 return root;
442 ✗ }
443
444 3056 bool LoadJsonFromString(const std::string& str, Json::Value& root, JsonRoot expected) {
445
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3056 times.
3056 Json::ValueType expectedType = (expected == JsonRoot::Array) ? Json::arrayValue : Json::objectValue;
446
447
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 2948 times.
3056 if (!LoadJsonFromString(str, root)) {
448
1/2
✓ Branch 0 taken 92 times.
✗ Branch 1 not taken.
92 root = Json::Value(expectedType);
449 92 return false;
450 }
451
452 // See LoadJsonFromFile() above - a null root is not a mismatch, it's
453 // normalized to an empty value of the expected shape and reported as
454 // success.
455
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2932 times.
2948 if (root.isNull()) {
456
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 root = Json::Value(expectedType);
457 16 return true;
458 }
459
460
2/2
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 2865 times.
2932 if (root.type() != expectedType) {
461 67 LogErr(VB_GENERAL, "JSON string has a %s at its root, expected %s - treating as empty\n",
462 JsonTypeName(root.type()), JsonTypeName(expectedType));
463
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 root = Json::Value(expectedType);
464 67 return false;
465 }
466
467 2865 return true;
468 }
469
470 ✗ Json::Value LoadJsonFromString(const std::string& str, JsonRoot expected) {
471 ✗ Json::Value root;
472
473 ✗ LoadJsonFromString(str, root, expected);
474
475 ✗ return root;
476 ✗ }
477
478 1473301 std::string SaveJsonToString(const Json::Value& root, const std::string& indentation) {
479
1/2
✓ Branch 0 taken 1473301 times.
✗ Branch 1 not taken.
1473301 Json::StreamWriterBuilder wbuilder;
480
3/6
✓ Branch 0 taken 1473301 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1473301 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1473301 times.
✗ Branch 5 not taken.
4419903 wbuilder["indentation"] = indentation;
481
482
1/2
✓ Branch 0 taken 1473301 times.
✗ Branch 1 not taken.
1473301 std::string result = Json::writeString(wbuilder, root);
483
484 1473301 return result;
485 1473301 }
486
487 16446 bool SaveJsonToString(const Json::Value& root, std::string& str, const std::string& indentation) {
488
1/2
✓ Branch 0 taken 16446 times.
✗ Branch 1 not taken.
16446 str = SaveJsonToString(root, indentation);
489
490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16446 times.
16446 if (str.empty())
491 ✗ return false;
492
493 16446 return true;
494 }
495
496 16446 bool SaveJsonToFile(const Json::Value& root, const std::string& filename, const std::string& indentation) {
497 16446 std::string str;
498
499
1/2
✓ Branch 0 taken 16446 times.
✗ Branch 1 not taken.
16446 bool result = SaveJsonToString(root, str, indentation);
500
501
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16446 times.
16446 if (!result) {
502 ✗ LogErr(VB_GENERAL, "Error converting Json::Value to std::string()\n");
503 ✗ return false;
504 }
505
506
1/2
✓ Branch 0 taken 16446 times.
✗ Branch 1 not taken.
16446 result = PutFileContents(filename, str);
507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16446 times.
16446 if (!result) {
508 ✗ return false;
509 }
510
511 16446 return true;
512 16446 }
513
514 ✗ bool SaveJsonToFile(const Json::Value& root, const char* filename, const char* indentation) {
515 ✗ std::string filenameStr = filename;
516 ✗ std::string indentationStr = indentation;
517
518 ✗ return SaveJsonToFile(root, filenameStr, indentationStr);
519 ✗ }
520
521 ✗ std::string getSimpleHTMLTTag(const std::string& html, const std::string& searchStr, const std::string& skipStr, const std::string& endStr) {
522 ✗ std::string value;
523 ✗ std::string tStr;
524 ✗ std::size_t fStart = html.find(searchStr);
525 std::size_t fEnd;
526
527 ✗ if (fStart != std::string::npos) {
528 ✗ tStr = html.substr(fStart + searchStr.size());
529 ✗ fStart = tStr.find(skipStr);
530 ✗ if (fStart != std::string::npos) {
531 ✗ fStart += skipStr.size();
532 ✗ fEnd = tStr.substr(fStart).find(endStr);
533 ✗ fEnd += fStart;
534 ✗ if (fEnd > fStart) {
535 ✗ value = tStr.substr(fStart, fEnd - fStart);
536 ✗ TrimWhiteSpace(value);
537 ✗ replaceAll(value, std::string(" "), std::string(" "));
538 }
539 }
540 }
541
542 ✗ return value;
543 ✗ }
544
545 4 std::string getSimpleXMLTag(const std::string& xml, const std::string& tag) {
546 4 std::string value;
547
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 std::string sSearch = "<";
548
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 sSearch += tag + ">";
549
550
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 std::string eSearch = "</";
551
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 eSearch += tag + ">";
552
553 4 std::size_t fStart = xml.find(sSearch);
554 4 std::size_t fEnd = xml.find(eSearch);
555
556
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if ((fStart != std::string::npos) &&
557
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 (fEnd != std::string::npos) &&
558 (fEnd > fStart)) {
559
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 value = xml.substr(fStart + sSearch.length(), fEnd - fStart - sSearch.length());
560
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 TrimWhiteSpace(value);
561 }
562
563 4 return value;
564 4 }
565
566 // URL Helpers
567 20 size_t urlWriteData(void* buffer, size_t size, size_t nmemb, void* userp) {
568 20 std::string* str = (std::string*)userp;
569
570 20 str->append(static_cast<const char*>(buffer), size * nmemb);
571
572 20 return size * nmemb;
573 }
574
575 219 std::string NormalizeMacAddress(const std::string& mac) {
576 219 std::string out;
577
2/2
✓ Branch 0 taken 3560 times.
✓ Branch 1 taken 219 times.
3779 for (char c : mac) {
578
2/2
✓ Branch 0 taken 2545 times.
✓ Branch 1 taken 1015 times.
3560 if (isxdigit((unsigned char)c)) {
579
1/2
✓ Branch 0 taken 2545 times.
✗ Branch 1 not taken.
2545 out += toupper((unsigned char)c);
580
3/8
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1010 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1015 } else if (c != ':' && c != '-' && c != '.' && c != ' ') {
581 // Anything else means this was never a MAC.
582 ✗ return "";
583 }
584 }
585
6/8
✓ Branch 0 taken 212 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 212 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 212 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 212 times.
219 if (out.size() != 12 || out == "000000000000") {
586
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
14 return "";
587 }
588 212 return out;
589 219 }
590
591 4534 std::string GetMacForAddress(const std::string& address) {
592
4/8
✓ Branch 0 taken 4534 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4534 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4534 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4534 times.
4534 if (address.empty() || IsLoopbackAddress(address)) {
593 ✗ return "";
594 }
595 // IPv6 neighbours are not exposed through procfs, so a v6-only peer gets no
596 // MAC here. That is harmless: a dual-stacked device is still identified
597 // through its IPv4 entry, and the UI merges a device's addresses by the
598 // UUID any one of them carries.
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4534 times.
4534 if (address.find(':') != std::string::npos) {
600 ✗ return "";
601 }
602
603
1/2
✓ Branch 0 taken 4534 times.
✗ Branch 1 not taken.
4534 std::ifstream arp("/proc/net/arp");
604
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4534 times.
4534 if (!arp.is_open()) {
605 ✗ return "";
606 }
607 4534 std::string line;
608
1/2
✓ Branch 0 taken 4534 times.
✗ Branch 1 not taken.
4534 std::getline(arp, line); // header
609
4/6
✓ Branch 0 taken 13693 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13693 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9351 times.
✓ Branch 5 taken 4342 times.
13693 while (std::getline(arp, line)) {
610
1/2
✓ Branch 0 taken 9351 times.
✗ Branch 1 not taken.
9351 std::stringstream ss(line);
611 9351 std::string ip, hwType, flags, hwAddr;
612
6/12
✓ Branch 0 taken 9351 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9351 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9351 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 9351 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 9351 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 9351 times.
9351 if (!(ss >> ip >> hwType >> flags >> hwAddr)) {
613 ✗ continue;
614 }
615
2/2
✓ Branch 0 taken 9159 times.
✓ Branch 1 taken 192 times.
9351 if (ip != address) {
616 9159 continue;
617 }
618 // Flag bit 0x2 (ATF_COM) means the entry is complete. An incomplete
619 // entry carries a zeroed address that would otherwise look like a MAC.
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
192 if ((strtol(flags.c_str(), nullptr, 0) & 0x2) == 0) {
621 ✗ return "";
622 }
623
1/2
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
192 return NormalizeMacAddress(hwAddr);
624
10/10
✓ Branch 0 taken 9159 times.
✓ Branch 1 taken 192 times.
✓ Branch 2 taken 9159 times.
✓ Branch 3 taken 192 times.
✓ Branch 4 taken 9159 times.
✓ Branch 5 taken 192 times.
✓ Branch 6 taken 9159 times.
✓ Branch 7 taken 192 times.
✓ Branch 8 taken 9159 times.
✓ Branch 9 taken 192 times.
46755 }
625
1/2
✓ Branch 0 taken 4342 times.
✗ Branch 1 not taken.
8684 return "";
626 4534 }
627
628 22749 bool IsLoopbackAddress(const std::string& address) {
629
4/6
✓ Branch 0 taken 22749 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22749 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 22740 times.
45498 if (startsWith(address, "127.")) {
630 9 return true;
631 }
632 // "::1" is the usual spelling; the fully expanded form is legal too and
633 // shows up from some resolvers.
634
2/4
✓ Branch 0 taken 22740 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22740 times.
22740 return address == "::1" || address == "0:0:0:0:0:0:0:1";
635 }
636
637 // See the comment on the declarations in common.h.
638 namespace {
639 struct ResolveCacheEntry {
640 uint32_t addr = 0;
641 bool valid = false;
642 uint64_t expiresMS = 0;
643 };
644 std::mutex resolveCacheLock;
645 std::map<std::string, ResolveCacheEntry> resolveCache;
646
647 // Short enough that a peer coming online is noticed on the next poll of
648 // whatever is watching it, long enough to collapse a startup burst.
649 constexpr uint64_t RESOLVE_TTL_OK_MS = 60000;
650 constexpr uint64_t RESOLVE_TTL_FAIL_MS = 30000;
651 // Nothing should be resolving thousands of distinct names; if it is, drop the
652 // expired entries rather than growing without bound.
653 constexpr size_t RESOLVE_CACHE_MAX = 256;
654 } // namespace
655
656 11009 bool ResolveHostToIPv4(const std::string& host, uint32_t& addr) {
657
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11009 times.
11009 if (host.empty()) {
658 ✗ return false;
659 }
660 // A literal needs no resolver and must not take a cache slot -- the HTTP
661 // discovery scan walks whole /24s of them.
662 struct in_addr numeric;
663
2/2
✓ Branch 0 taken 10970 times.
✓ Branch 1 taken 39 times.
11009 if (inet_pton(AF_INET, host.c_str(), &numeric) == 1) {
664 10970 addr = numeric.s_addr;
665 10970 return true;
666 }
667
668
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 uint64_t now = (uint64_t)GetMonotonicTimeMS();
669 {
670
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 std::unique_lock<std::mutex> lock(resolveCacheLock);
671
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 auto it = resolveCache.find(host);
672
5/6
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 23 times.
✓ Branch 5 taken 16 times.
39 if (it != resolveCache.end() && it->second.expiresMS > now) {
673 23 addr = it->second.addr;
674 23 return it->second.valid;
675 }
676
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 23 times.
39 }
677
678 // Resolve with the lock released: this blocks for seconds on a name that
679 // does not exist, and holding the lock across it would hand that stall to
680 // every other thread resolving anything at all. Two threads racing on the
681 // same new name can both pay for it; that is cheaper than serialising all
682 // lookups behind one.
683 16 struct addrinfo hints{};
684 16 hints.ai_family = AF_INET; // IPv4 only -- callers want a dotted-quad
685 16 hints.ai_socktype = SOCK_DGRAM; // one result per address, not per socktype
686 16 struct addrinfo* res = nullptr;
687 16 bool valid = false;
688 16 uint32_t resolved = 0;
689
6/8
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 13 times.
16 if (getaddrinfo(host.c_str(), nullptr, &hints, &res) == 0 && res != nullptr) {
690 3 resolved = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr;
691 3 valid = true;
692 }
693
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
16 if (res) {
694 3 freeaddrinfo(res);
695 }
696
697 {
698
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 std::unique_lock<std::mutex> lock(resolveCacheLock);
699
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (resolveCache.size() >= RESOLVE_CACHE_MAX) {
700 ✗ uint64_t cutoff = (uint64_t)GetMonotonicTimeMS();
701 ✗ for (auto it = resolveCache.begin(); it != resolveCache.end();) {
702 ✗ it = (it->second.expiresMS <= cutoff) ? resolveCache.erase(it) : std::next(it);
703 }
704 }
705
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 ResolveCacheEntry& e = resolveCache[host];
706 16 e.addr = resolved;
707 16 e.valid = valid;
708
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
16 e.expiresMS = now + (valid ? RESOLVE_TTL_OK_MS : RESOLVE_TTL_FAIL_MS);
709 16 }
710
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
16 if (valid) {
711 3 addr = resolved;
712 }
713 16 return valid;
714 }
715
716 10995 std::string ResolveHostToIPv4(const std::string& host) {
717 10995 uint32_t addr = 0;
718
3/4
✓ Branch 0 taken 10995 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 10974 times.
10995 if (!ResolveHostToIPv4(host, addr)) {
719
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
42 return "";
720 }
721 struct in_addr ia;
722 10974 ia.s_addr = addr;
723 10974 char buf[INET_ADDRSTRLEN] = { 0 };
724
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10974 times.
10974 if (!inet_ntop(AF_INET, &ia, buf, sizeof(buf))) {
725 ✗ return "";
726 }
727
1/2
✓ Branch 0 taken 10974 times.
✗ Branch 1 not taken.
21948 return buf;
728 }
729
730 1673 void FlushHostResolveCache() {
731
1/2
✓ Branch 0 taken 1673 times.
✗ Branch 1 not taken.
1673 std::unique_lock<std::mutex> lock(resolveCacheLock);
732 1673 resolveCache.clear();
733 1673 }
734
735 663 std::string buildHttpURL(const std::string& address, const std::string& path) {
736
1/2
✓ Branch 0 taken 663 times.
✗ Branch 1 not taken.
663 std::string host = address;
737 // An IPv6 literal contains ':' (hostnames and IPv4 never do). curl requires
738 // it bracketed, and a link-local zone id ("fe80::1%eth0") must have its '%'
739 // percent-encoded as "%25" inside a URL.
740
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 653 times.
663 if (host.find(':') != std::string::npos) {
741 10 std::string::size_type pct = host.find('%');
742
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 if (pct != std::string::npos) {
743
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
8 host = host.substr(0, pct) + "%25" + host.substr(pct + 1);
744 }
745
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 host = "[" + host + "]";
746 }
747
2/4
✓ Branch 0 taken 663 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 663 times.
✗ Branch 3 not taken.
1326 return "http://" + host + path;
748 663 }
749
750 // DEPRECATED -- see the comment on the declarations in common.h. Kept for
751 // external plugins and for the callers that have no main loop to complete a
752 // CurlManager request against.
753 39 bool urlHelper(const std::string method, const std::string& url, const std::string& data, std::string& resp, const unsigned int timeout) {
754
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 return urlHelper(method, url, data, resp, std::list<std::string>(), timeout);
755 }
756 39 bool urlHelper(const std::string method, const std::string& url, const std::string& data, std::string& resp, const std::list<std::string>& extraHeaders, const unsigned int timeout) {
757
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 CURL* curl = curl_easy_init();
758
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 std::string userAgent = "FPP/";
759
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 userAgent += getFPPVersionTriplet();
760
761
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 resp = "";
762
763
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (!curl) {
764 ✗ LogDebug(VB_GENERAL, "Unable to create curl instance in urlHelper()\n");
765 ✗ return false;
766 }
767
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 SetCurlCloseOnExec(curl);
768
769 CURLcode status;
770
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 status = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, urlWriteData);
771
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (status != CURLE_OK) {
772 ✗ LogErr(VB_GENERAL, "curl_easy_setopt() Error setting write callback function: %s\n", curl_easy_strerror(status));
773 ✗ curl_easy_cleanup(curl);
774 ✗ return false;
775 }
776
777
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 status = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
778
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (status != CURLE_OK) {
779 ✗ LogErr(VB_GENERAL, "curl_easy_setopt() Error setting class pointer: %s\n", curl_easy_strerror(status));
780 ✗ curl_easy_cleanup(curl);
781 ✗ return false;
782 }
783
784
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 status = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
785
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 if (status != CURLE_OK) {
786 ✗ LogErr(VB_GENERAL, "curl_easy_setopt() Error setting URL: %s\n", curl_easy_strerror(status));
787 ✗ curl_easy_cleanup(curl);
788 ✗ return false;
789 }
790
791 39 struct curl_slist* headers = NULL;
792
15/30
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 33 times.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
✓ Branch 13 taken 33 times.
✓ Branch 14 taken 6 times.
✓ Branch 15 taken 33 times.
✓ Branch 16 taken 39 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 39 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 6 times.
✓ Branch 21 taken 33 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
129 if (startsWith(data, "{") && endsWith(data, "}")) {
793
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 headers = curl_slist_append(headers, "Accept: application/json");
794
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 headers = curl_slist_append(headers, "Content-Type: application/json");
795 }
796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
39 for (auto& h : extraHeaders) {
797 ✗ headers = curl_slist_append(headers, h.c_str());
798 }
799
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 33 times.
39 if (headers) {
800
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
801 }
802
803
8/10
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 28 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 31 times.
✓ Branch 9 taken 8 times.
39 if ((method == "POST") || (method == "PUT")) {
804 // The size first: without it curl takes strlen() of the body, which
805 // cuts a binary body short at its first zero byte.
806
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)data.size());
807
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 status = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
808
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (status != CURLE_OK) {
809 ✗ LogErr(VB_GENERAL, "curl_easy_setopt() Error setting postfields data: %s\n", curl_easy_strerror(status));
810 ✗ curl_slist_free_all(headers);
811 ✗ curl_easy_cleanup(curl);
812 ✗ return false;
813 }
814 }
815
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
816
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 2);
817
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
818 // Accept (and transparently decode) whatever encodings this libcurl was built
819 // with. Some controllers gzip a response whether or not the client asked for
820 // it, and without this the caller is handed the compressed bytes and every
821 // find()/JSON parse on them quietly fails. CurlManager has always done this;
822 // this path had not, which is what made such a device undetectable.
823
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
824
825
3/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 36 times.
39 if (method == "POST")
826
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 curl_easy_setopt(curl, CURLOPT_POST, 1);
827
3/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 8 times.
36 else if (method == "PUT")
828
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
829
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 else if (method == "DELETE")
830 ✗ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
831
832
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
833
834
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 LogDebug(VB_GENERAL, "Calling %s %s\n", method.c_str(), url.c_str());
835
836
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 status = curl_easy_perform(curl);
837
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 18 times.
39 if (status != CURLE_OK) {
838
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
21 LogErr(VB_GENERAL, "curl_easy_perform() failed (%s): %s\n", url.c_str(), curl_easy_strerror(status));
839
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 curl_slist_free_all(headers);
840
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 curl_easy_cleanup(curl);
841 21 return false;
842 }
843
844
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 LogDebug(VB_GENERAL, "%s %s resp: %s\n", method.c_str(), url.c_str(), resp.c_str());
845
846
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 curl_slist_free_all(headers);
847
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 curl_easy_cleanup(curl);
848 18 return true;
849 39 }
850
851 8 bool urlHelper(const std::string method, const std::string& url, std::string& resp, const unsigned int timeout) {
852 8 std::string data;
853
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
16 return urlHelper(method, url, data, resp, timeout);
854 8 }
855
856 8 bool urlGet(const std::string url, std::string& resp) {
857 8 std::string data;
858
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
16 return urlHelper("GET", url, resp);
859 8 }
860
861 3 bool urlPost(const std::string url, const std::string data, std::string& resp) {
862
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
6 return urlHelper("POST", url, data, resp);
863 }
864
865 28 bool urlPut(const std::string url, const std::string data, std::string& resp) {
866
2/4
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
56 return urlHelper("PUT", url, data, resp);
867 }
868
869 ✗ bool urlDelete(const std::string url, const std::string data, std::string& resp) {
870 ✗ return urlHelper("DELETE", url, data, resp);
871 }
872
873 ✗ bool urlDelete(const std::string url, std::string& resp) {
874 ✗ std::string data;
875 ✗ return urlHelper("DELETE", url, data, resp);
876 ✗ }
877
878 static const std::string BASE64_CHARS =
879 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
880 "abcdefghijklmnopqrstuvwxyz"
881 "0123456789+/";
882
883 191 static inline bool isBase64(uint8_t c) {
884
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 190 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
191 return (isalnum(c) || (c == '+') || (c == '/'));
885 }
886
887 14 std::string base64Encode(uint8_t const* buf, unsigned int bufLen) {
888 14 std::string ret;
889 14 int i = 0;
890 14 int j = 0;
891 uint8_t char_array_3[3];
892 uint8_t char_array_4[4];
893
894
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 14 times.
294 while (bufLen--) {
895 280 char_array_3[i++] = *(buf++);
896
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 196 times.
280 if (i == 3) {
897 84 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
898 84 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
899 84 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
900 84 char_array_4[3] = char_array_3[2] & 0x3f;
901
902
2/2
✓ Branch 0 taken 336 times.
✓ Branch 1 taken 84 times.
420 for (i = 0; (i < 4); i++) {
903
1/2
✓ Branch 0 taken 336 times.
✗ Branch 1 not taken.
336 ret += BASE64_CHARS[char_array_4[i]];
904 }
905 84 i = 0;
906 }
907 }
908
909
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (i) {
910
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
28 for (j = i; j < 3; j++) {
911 14 char_array_3[j] = '\0';
912 }
913
914 14 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
915 14 char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
916 14 char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
917 14 char_array_4[3] = char_array_3[2] & 0x3f;
918
919
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 14 times.
56 for (j = 0; (j < i + 1); j++) {
920
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 ret += BASE64_CHARS[char_array_4[j]];
921 }
922
923
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
28 while ((i++ < 3)) {
924
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 ret += '=';
925 }
926 }
927 14 return ret;
928 ✗ }
929 18 std::vector<uint8_t> base64Decode(std::string const& encodedString) {
930 18 int in_len = encodedString.size();
931 18 int i = 0;
932 18 int j = 0;
933 18 int in_ = 0;
934 uint8_t char_array_4[4], char_array_3[3];
935 18 std::vector<uint8_t> ret;
936
937
7/8
✓ Branch 0 taken 208 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 191 times.
✓ Branch 3 taken 17 times.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 191 times.
✓ Branch 7 taken 18 times.
209 while (in_len-- && (encodedString[in_] != '=') && isBase64(encodedString[in_])) {
938 191 char_array_4[i++] = encodedString[in_];
939 191 in_++;
940
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 156 times.
191 if (i == 4) {
941
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 35 times.
175 for (i = 0; i < 4; i++) {
942 140 char_array_4[i] = BASE64_CHARS.find(char_array_4[i]);
943 }
944
945 35 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
946 35 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
947 35 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
948
949
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 35 times.
140 for (i = 0; (i < 3); i++) {
950
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
105 ret.push_back(char_array_3[i]);
951 }
952 35 i = 0;
953 }
954 }
955
956
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 times.
18 if (i) {
957
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
34 for (j = i; j < 4; j++) {
958 17 char_array_4[j] = 0;
959 }
960
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 17 times.
85 for (j = 0; j < 4; j++) {
961 68 char_array_4[j] = BASE64_CHARS.find(char_array_4[j]);
962 }
963
964 17 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
965 17 char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
966 17 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
967
968
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 17 times.
51 for (j = 0; (j < i - 1); j++) {
969
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 ret.push_back(char_array_3[j]);
970 }
971 }
972 18 return ret;
973 ✗ }
974
975 static std::function<void(bool)> SHUTDOWN_HOOK;
976 static std::atomic<bool> RESTART_RESUME_PLAYLIST{ false };
977 13 void ShutdownFPPD(bool restart) {
978
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (SHUTDOWN_HOOK) {
979 13 SHUTDOWN_HOOK(restart);
980 }
981 13 }
982 4 void RestartFPPDResumingPlaylist() {
983 4 RESTART_RESUME_PLAYLIST = true;
984 4 ShutdownFPPD(true);
985 4 }
986 12 bool RestartShouldResumePlaylist() {
987 12 return RESTART_RESUME_PLAYLIST;
988 }
989 1887 void RegisterShutdownHandler(const std::function<void(bool)> hook) {
990 1887 SHUTDOWN_HOOK = hook;
991 1887 }
992
993 static std::function<std::string(std::string&)> MANUAL_CRASH_REPORT_HOOK;
994 4 std::string BuildManualCrashReport(std::string& error) {
995
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (!MANUAL_CRASH_REPORT_HOOK) {
996 ✗ error = "unavailable";
997 ✗ return "";
998 }
999 4 return MANUAL_CRASH_REPORT_HOOK(error);
1000 }
1001 1887 void RegisterManualCrashReportHandler(const std::function<std::string(std::string&)> hook) {
1002 1887 MANUAL_CRASH_REPORT_HOOK = hook;
1003 1887 }
1004
1005 static std::atomic<const char*> MAIN_LOOP_PHASE_STR{ "startup" };
1006 3717740 void SetMainLoopPhase(const char* phase) {
1007 3717740 MAIN_LOOP_PHASE_STR.store(phase, std::memory_order_relaxed);
1008 3717740 }
1009 22 const char* GetMainLoopPhase() {
1010 22 const char* p = MAIN_LOOP_PHASE_STR.load(std::memory_order_relaxed);
1011
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 return p ? p : "?";
1012 }
1013
1014 5832 std::string GetFileExtension(const std::string& filename) {
1015
2/2
✓ Branch 0 taken 2476 times.
✓ Branch 1 taken 3356 times.
5832 if (filename.find_last_of(".") != std::string::npos)
1016 2476 return filename.substr(filename.find_last_of(".") + 1);
1017
1/2
✓ Branch 0 taken 3356 times.
✗ Branch 1 not taken.
6712 return "";
1018 }
1019
1020 271250 void SetThreadName(const std::string& name) {
1021 #ifdef PLATFORM_OSX
1022 pthread_setname_np(name.c_str());
1023 #else
1024 271250 pthread_setname_np(pthread_self(), name.c_str());
1025 #endif
1026 271250 }
1027
1028 2805 bool SetThreadRealtimePriority(int priority) {
1029 #ifndef PLATFORM_OSX
1030 // Kernel RT throttling (sched_rt_runtime_us, default 95%) still guarantees
1031 // normal-priority threads a CPU share even if an RT thread runs hot.
1032 // Returns false (typically EPERM in containers/non-root) so callers can
1033 // log which mode they're running in.
1034 struct sched_param param;
1035 2805 param.sched_priority = priority;
1036 2805 return pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) == 0;
1037 #else
1038 return false;
1039 #endif
1040 }
1041
1042 1885 std::string getPlatform() {
1043
2/4
✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1885 times.
✗ Branch 3 not taken.
1885 std::string platform = GetFileContents("/etc/fpp/platform");
1044
1/2
✓ Branch 0 taken 1885 times.
✗ Branch 1 not taken.
1885 TrimWhiteSpace(platform);
1045 1885 return platform;
1046 ✗ }
1047
1048
1049 /////////////////////////////////////////////////////////////////////////////
1050 // Unrolled 32x32 bitwise transform from Hacker's Delight
1051 //
1052 // Bit flips an array along the diagonal from MSbit on LS uint32_t to LS bit on MS uint32_t
1053 //
1054 // src[00] = 0b00000000000000000000000000000001
1055 // src[01] = 0b00000000000000000000000000000010
1056 // ...
1057 // src[30] = 0b00000000000000000000000000000000
1058 // src[31] = 0b00000000000000000000000000000000
1059 // becomes
1060 // dst[00] = 0b00000000000000000000000000000000
1061 // dst[01] = 0b00000000000000000000000000000000
1062 // ...
1063 // dst[30] = 0b01000000000000000000000000000000
1064 // dst[31] = 0b10000000000000000000000000000000
1065
1066 #define tr32swap(a0, a1, j, m) t = (a0 ^ (a1 >> j)) & m; \
1067 a0 = a0 ^ t; \
1068 a1 = a1 ^ (t << j);
1069
1070 40088 void TransposeBits32x32(uint32_t *dst, uint32_t *src) {
1071 uint32_t m, t;
1072 uint32_t a0, a1, a2, a3, a4, a5, a6, a7,
1073 a8, a9, a10, a11, a12, a13, a14, a15,
1074 a16, a17, a18, a19, a20, a21, a22, a23,
1075 a24, a25, a26, a27, a28, a29, a30, a31;
1076
1077 40088 a0 = src[ 0]; a1 = src[ 1]; a2 = src[ 2]; a3 = src[ 3];
1078 40088 a4 = src[ 4]; a5 = src[ 5]; a6 = src[ 6]; a7 = src[ 7];
1079 40088 a8 = src[ 8]; a9 = src[ 9]; a10 = src[10]; a11 = src[11];
1080 40088 a12 = src[12]; a13 = src[13]; a14 = src[14]; a15 = src[15];
1081 40088 a16 = src[16]; a17 = src[17]; a18 = src[18]; a19 = src[19];
1082 40088 a20 = src[20]; a21 = src[21]; a22 = src[22]; a23 = src[23];
1083 40088 a24 = src[24]; a25 = src[25]; a26 = src[26]; a27 = src[27];
1084 40088 a28 = src[28]; a29 = src[29]; a30 = src[30]; a31 = src[31];
1085
1086 40088 m = 0x0000FFFF;
1087 40088 tr32swap(a0, a16, 16, m)
1088 40088 tr32swap(a1, a17, 16, m)
1089 40088 tr32swap(a2, a18, 16, m)
1090 40088 tr32swap(a3, a19, 16, m)
1091 40088 tr32swap(a4, a20, 16, m)
1092 40088 tr32swap(a5, a21, 16, m)
1093 40088 tr32swap(a6, a22, 16, m)
1094 40088 tr32swap(a7, a23, 16, m)
1095 40088 tr32swap(a8, a24, 16, m)
1096 40088 tr32swap(a9, a25, 16, m)
1097 40088 tr32swap(a10, a26, 16, m)
1098 40088 tr32swap(a11, a27, 16, m)
1099 40088 tr32swap(a12, a28, 16, m)
1100 40088 tr32swap(a13, a29, 16, m)
1101 40088 tr32swap(a14, a30, 16, m)
1102 40088 tr32swap(a15, a31, 16, m)
1103
1104 40088 m = 0x00FF00FF;
1105 40088 tr32swap(a0, a8, 8, m)
1106 40088 tr32swap(a1, a9, 8, m)
1107 40088 tr32swap(a2, a10, 8, m)
1108 40088 tr32swap(a3, a11, 8, m)
1109 40088 tr32swap(a4, a12, 8, m)
1110 40088 tr32swap(a5, a13, 8, m)
1111 40088 tr32swap(a6, a14, 8, m)
1112 40088 tr32swap(a7, a15, 8, m)
1113 40088 tr32swap(a16, a24, 8, m)
1114 40088 tr32swap(a17, a25, 8, m)
1115 40088 tr32swap(a18, a26, 8, m)
1116 40088 tr32swap(a19, a27, 8, m)
1117 40088 tr32swap(a20, a28, 8, m)
1118 40088 tr32swap(a21, a29, 8, m)
1119 40088 tr32swap(a22, a30, 8, m)
1120 40088 tr32swap(a23, a31, 8, m)
1121
1122 40088 m = 0x0F0F0F0F;
1123 40088 tr32swap(a0, a4, 4, m)
1124 40088 tr32swap(a1, a5, 4, m)
1125 40088 tr32swap(a2, a6, 4, m)
1126 40088 tr32swap(a3, a7, 4, m)
1127 40088 tr32swap(a8, a12, 4, m)
1128 40088 tr32swap(a9, a13, 4, m)
1129 40088 tr32swap(a10, a14, 4, m)
1130 40088 tr32swap(a11, a15, 4, m)
1131 40088 tr32swap(a16, a20, 4, m)
1132 40088 tr32swap(a17, a21, 4, m)
1133 40088 tr32swap(a18, a22, 4, m)
1134 40088 tr32swap(a19, a23, 4, m)
1135 40088 tr32swap(a24, a28, 4, m)
1136 40088 tr32swap(a25, a29, 4, m)
1137 40088 tr32swap(a26, a30, 4, m)
1138 40088 tr32swap(a27, a31, 4, m)
1139
1140 40088 m = 0x33333333;
1141 40088 tr32swap(a0, a2, 2, m)
1142 40088 tr32swap(a1, a3, 2, m)
1143 40088 tr32swap(a4, a6, 2, m)
1144 40088 tr32swap(a5, a7, 2, m)
1145 40088 tr32swap(a8, a10, 2, m)
1146 40088 tr32swap(a9, a11, 2, m)
1147 40088 tr32swap(a12, a14, 2, m)
1148 40088 tr32swap(a13, a15, 2, m)
1149 40088 tr32swap(a16, a18, 2, m)
1150 40088 tr32swap(a17, a19, 2, m)
1151 40088 tr32swap(a20, a22, 2, m)
1152 40088 tr32swap(a21, a23, 2, m)
1153 40088 tr32swap(a24, a26, 2, m)
1154 40088 tr32swap(a25, a27, 2, m)
1155 40088 tr32swap(a28, a30, 2, m)
1156 40088 tr32swap(a29, a31, 2, m)
1157
1158 40088 m = 0x55555555;
1159 40088 tr32swap(a0, a1, 1, m)
1160 40088 tr32swap(a2, a3, 1, m)
1161 40088 tr32swap(a4, a5, 1, m)
1162 40088 tr32swap(a6, a7, 1, m)
1163 40088 tr32swap(a8, a9, 1, m)
1164 40088 tr32swap(a10, a11, 1, m)
1165 40088 tr32swap(a12, a13, 1, m)
1166 40088 tr32swap(a14, a15, 1, m)
1167 40088 tr32swap(a16, a17, 1, m)
1168 40088 tr32swap(a18, a19, 1, m)
1169 40088 tr32swap(a20, a21, 1, m)
1170 40088 tr32swap(a22, a23, 1, m)
1171 40088 tr32swap(a24, a25, 1, m)
1172 40088 tr32swap(a26, a27, 1, m)
1173 40088 tr32swap(a28, a29, 1, m)
1174 40088 tr32swap(a30, a31, 1, m)
1175
1176 40088 dst[ 0] = a0; dst[ 1] = a1; dst[ 2] = a2; dst[ 3] = a3;
1177 40088 dst[ 4] = a4; dst[ 5] = a5; dst[ 6] = a6; dst[ 7] = a7;
1178 40088 dst[ 8] = a8; dst[ 9] = a9; dst[10] = a10; dst[11] = a11;
1179 40088 dst[12] = a12; dst[13] = a13; dst[14] = a14; dst[15] = a15;
1180 40088 dst[16] = a16; dst[17] = a17; dst[18] = a18; dst[19] = a19;
1181 40088 dst[20] = a20; dst[21] = a21; dst[22] = a22; dst[23] = a23;
1182 40088 dst[24] = a24; dst[25] = a25; dst[26] = a26; dst[27] = a27;
1183 40088 dst[28] = a28; dst[29] = a29; dst[30] = a30; dst[31] = a31;
1184 40088 }
1185 /////////////////////////////////////////////////////////////////////////////
1186
1187