1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2010 Licq Developers <licq-dev@googlegroups.com>
4  *
5  * Please refer to the COPYRIGHT file distributed with this source
6  * distribution for the names of the individual contributors.
7  *
8  * Licq is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Licq is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Licq; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include <licq/logging/logutils.h>
24 
25 #include <cstdio>
26 #include <iomanip>
27 #include <sstream>
28 
29 extern const unsigned int PacketBit;
30 
31 using namespace Licq;
32 
levelInBitmask(Log::Level level,unsigned int bitmask)33 bool LogUtils::levelInBitmask(Log::Level level, unsigned int bitmask)
34 {
35   return bitmask & (1 << level);
36 }
37 
packetInBitmask(unsigned int bitmask)38 bool LogUtils::packetInBitmask(unsigned int bitmask)
39 {
40   return bitmask & PacketBit;
41 }
42 
convertOldBitmaskToNew(int levels)43 unsigned int LogUtils::convertOldBitmaskToNew(int levels)
44 {
45   unsigned int mask = 0;
46   mask |= (levels & 0x1) ? (1 << Log::Info) : 0;
47   mask |= (levels & 0x2) ? (1 << Log::Unknown) : 0;
48   mask |= (levels & 0x4) ? (1 << Log::Error) : 0;
49   mask |= (levels & 0x8) ? (1 << Log::Warning) : 0;
50 
51   mask |= (levels & 0x10) ? (1 << Log::Debug) : 0;
52   mask |= (levels & 0x10) ? PacketBit : 0;
53 
54   return mask;
55 }
56 
levelToString(Log::Level level)57 const char* LogUtils::levelToString(Log::Level level)
58 {
59   switch (level)
60   {
61     case Log::Unknown:
62       return "unknown";
63     case Log::Info:
64       return "info";
65     case Log::Warning:
66       return "warning";
67     case Log::Error:
68       return "error";
69     case Log::Debug:
70       return "debug";
71   }
72   return "?????";
73 }
74 
levelToShortString(Log::Level level)75 const char* LogUtils::levelToShortString(Log::Level level)
76 {
77   switch (level)
78   {
79     case Log::Unknown:
80       return "UNK";
81     case Log::Info:
82       return "INF";
83     case Log::Warning:
84       return "WAR";
85     case Log::Error:
86       return "ERR";
87     case Log::Debug:
88       return "DBG";
89   }
90   return "???";
91 }
92 
timeToString(const LogSink::Message::Time & msgTime)93 std::string LogUtils::timeToString(const LogSink::Message::Time& msgTime)
94 {
95   time_t t = msgTime.sec;
96   tm time;
97   ::localtime_r(&t, &time);
98 
99   char buffer[8 + 4 + 1];
100   ::strftime(buffer, 8 + 1, "%H:%M:%S", &time);
101   ::snprintf(&buffer[8], 4 + 1, ".%03u", msgTime.msec);
102 
103   return buffer;
104 }
105 
packetToString(std::ostream & os,LogSink::Message::Ptr message)106 static std::ostream& packetToString(
107     std::ostream& os, LogSink::Message::Ptr message)
108 {
109   const std::vector<uint8_t>& packet = message->packet;
110 
111   const size_t bytesPerRow = 16;
112   const size_t maxRows = 512;
113   const size_t bytesToPrint = std::min(bytesPerRow * maxRows, packet.size());
114 
115   const std::string prefix(5, ' ');
116 
117   char ascii[bytesPerRow + 1];
118   ascii[bytesPerRow] = '\0';
119 
120   // Save current flags
121   const std::ios_base::fmtflags flags = os.flags();
122 
123   os << std::hex << std::uppercase;
124   using std::setw; using std::setfill;
125 
126   for (size_t addr = 0; addr < bytesToPrint; addr++)
127   {
128     const size_t pos = addr % bytesPerRow;
129 
130     // Print the address at the start of the row
131     if (pos == 0)
132       os << prefix << setw(4) << setfill('0') << addr << ':';
133     // Or an extra space in the middle
134     else if (pos == (bytesPerRow / 2))
135       os << ' ';
136 
137     const uint8_t byte = packet[addr];
138 
139     // Print the byte in hex
140     os << ' ' << setw(2) << static_cast<uint16_t>(byte);
141 
142     // Save the ascii representation (if available; otherwise '.')
143     ascii[pos] = std::isprint(byte) ? byte : '.';
144 
145     // Print the ascii version at the end of the row
146     if (pos + 1 == bytesPerRow || addr + 1 == bytesToPrint)
147     {
148       ascii[pos + 1] = '\0';
149 
150       // Number of bytes needed for a "full" row
151       const size_t padBytes = bytesPerRow - (pos + 1);
152 
153       // Print 3 spaces for each missing byte
154       size_t padding = padBytes * 3;
155 
156       // Add one extra space to compensate for the extra space in the middle
157       if (pos <= (bytesPerRow / 2))
158         padding += 1;
159 
160       // Separate hex and ascii version with 3 spaces
161       padding += 3;
162 
163       os << std::string(padding, ' ') << ascii;
164 
165       // Print newline on all but the last line
166       if (addr + 1 != bytesToPrint)
167         os << "\n";
168     }
169   }
170 
171   // Print the address range for bytes not printed
172   if (bytesToPrint != packet.size())
173   {
174     os << "\n" << prefix << setw(4) << setfill('0') << bytesToPrint
175        << " - " << setw(4) << packet.size() - 1 << ": ...";
176   }
177 
178   // Restore flags
179   os.flags(flags);
180   return os;
181 }
182 
packetToString(LogSink::Message::Ptr message)183 std::string LogUtils::packetToString(LogSink::Message::Ptr message)
184 {
185   std::ostringstream ss;
186   ::packetToString(ss, message);
187   return ss.str();
188 }
189