1 /***************************************************************************
2                           rkrbackendprotocol  -  description
3                              -------------------
4     begin                : Thu Nov 04 2010
5     copyright            : (C) 2010, 2011, 2013, 2017 by Thomas Friedrichsmeier
6     email                : thomas.friedrichsmeier@kdemail.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "rkrbackendprotocol_shared.h"
19 
20 #include "../debug.h"
21 
RCommandProxy(const QString & command,int type)22 RCommandProxy::RCommandProxy (const QString &command, int type) {
23 	RK_TRACE (RBACKEND);
24 
25 	RCommandProxy::command = command;
26 	RCommandProxy::type = type;
27 	id = -1;
28 	status = 0;
29 }
30 
~RCommandProxy()31 RCommandProxy::~RCommandProxy () {
32 	RK_TRACE (RBACKEND);
33 
34 	RK_ASSERT ((type & RCommand::Internal) || (getDataType () == RData::NoData));
35 }
36 
37 
38 int RBackendRequest::_id = 0;
RBackendRequest(bool synchronous,RCallbackType type)39 RBackendRequest::RBackendRequest (bool synchronous, RCallbackType type) {
40 	RK_TRACE (RBACKEND);
41 
42 	RBackendRequest::synchronous = synchronous;
43 	RBackendRequest::type = type;
44 	id = ++_id;
45 	done = false;
46 	command = 0;
47 	output = 0;
48 }
49 
~RBackendRequest()50 RBackendRequest::~RBackendRequest () {
51 	RK_TRACE (RBACKEND);
52 
53 	delete command;
54 	delete output;
55 };
56 
mergeReply(RBackendRequest * reply)57 void RBackendRequest::mergeReply (RBackendRequest *reply) {
58 	RK_TRACE (RBACKEND);
59 
60 	RK_ASSERT (reply->id == id);
61 	command = reply->command;
62 	params = reply->params;
63 	output = reply->output;
64 	reply->command = 0;
65 	reply->output = 0;
66 }
67 
duplicate()68 RBackendRequest* RBackendRequest::duplicate () {
69 	RK_TRACE (RBACKEND);
70 
71 	RBackendRequest* ret = new RBackendRequest (synchronous, type);
72 	--_id;   // for pretty, consecutive numbering
73 	ret->id = id;
74 	ret->done = done;
75 	ret->command = command;
76 	ret->params = params;
77 	ret->output = output;
78 	// prevent double deletion issues
79 	command = 0;
80 	output = 0;
81 	return ret;
82 }
83 
84 
85 
86 #define MAX_BUF_LENGTH 16000
87 #define OUTPUT_STRING_RESERVE 1000
88 
RKROutputBuffer()89 RKROutputBuffer::RKROutputBuffer () {
90 	RK_TRACE (RBACKEND);
91 
92 	out_buf_len = 0;
93 }
94 
~RKROutputBuffer()95 RKROutputBuffer::~RKROutputBuffer () {
96 	RK_TRACE (RBACKEND);
97 
98 	if (!output_captures.isEmpty ()) RK_DEBUG (RBACKEND, DL_WARNING, "%d requests for recording output still active on interface shutdown", output_captures.size ());
99 }
100 
pushOutputCapture(int capture_mode)101 void RKROutputBuffer::pushOutputCapture (int capture_mode) {
102 	RK_TRACE (RBACKEND);
103 
104 	OutputCapture capture;
105 	capture.mode = capture_mode;
106 	output_captures.append (capture);
107 }
108 
popOutputCapture(bool highlighted)109 QString RKROutputBuffer::popOutputCapture (bool highlighted) {
110 	RK_TRACE (RBACKEND);
111 
112 	if (output_captures.isEmpty ()) {
113 		RK_ASSERT (!output_captures.isEmpty ());
114 		return QString ();
115 	}
116 	OutputCapture capture = output_captures.takeLast ();
117 	if (capture.recorded.isEmpty ()) return QString ();
118 
119 	QString ret;
120 	ROutput::ROutputType previous_type = ROutput::NoOutput;
121 	for (int i = 0; i < capture.recorded.length (); ++i) {
122 		const ROutput * output = capture.recorded[i];
123 		if (output->output.isEmpty ()) continue;
124 
125 		if (output->type != ROutput::Error) {	// NOTE: skip error output. It has already been written as a warning.
126 			if (highlighted && (output->type != previous_type)) {
127 				if (!ret.isEmpty ()) ret.append ("</pre>\n");
128 
129 				if (output->type == ROutput::Output) ret.append ("<pre class=\"output_normal\">");
130 				else if (output->type == ROutput::Warning) ret.append ("<pre class=\"output_warning\">");
131 				else {
132 					RK_ASSERT (false);
133 					ret.append ("<pre>");
134 				}
135 			}
136 			if (highlighted) ret.append (output->output.toHtmlEscaped ());
137 			else ret.append (output->output);
138 
139 			previous_type = output->type;
140 		}
141 	}
142 	if (highlighted && !ret.isEmpty ()) ret.append ("</pre>\n");
143 	return ret;
144 }
145 
appendToOutputList(ROutputList * list,const QString & output,ROutput::ROutputType output_type)146 void appendToOutputList (ROutputList *list, const QString &output, ROutput::ROutputType output_type) {
147 // No trace
148 	ROutput *current_output = 0;
149 	if (!list->isEmpty ()) {
150 		// Merge with previous output fragment, if of the same type
151 		current_output = list->last ();
152 		if (current_output->type != output_type) current_output = 0;
153 	}
154 	if (!current_output) {
155 		current_output = new ROutput;
156 		current_output->type = output_type;
157 		current_output->output.reserve (OUTPUT_STRING_RESERVE);
158 		list->append (current_output);
159 	}
160 	current_output->output.append (output);
161 }
162 
handleOutput(const QString & output,int buf_length,ROutput::ROutputType output_type,bool allow_blocking)163 bool RKROutputBuffer::handleOutput (const QString &output, int buf_length, ROutput::ROutputType output_type, bool allow_blocking) {
164 	if (!buf_length) return false;
165 	RK_TRACE (RBACKEND);
166 	RK_DEBUG (RBACKEND, DL_DEBUG, "Output type %d: %s", output_type, qPrintable (output));
167 
168 	// wait while the output buffer is exceeded to give downstream threads a chance to catch up
169 	while ((out_buf_len > MAX_BUF_LENGTH) && allow_blocking) {
170 		if (!doMSleep (10)) break;
171 	}
172 
173 	QMutexLocker lock (&output_buffer_mutex);
174 	bool previously_empty = (out_buf_len <= 0);
175 
176 	for (int i = output_captures.length () - 1; i >= 0; --i) {
177 		OutputCapture &cap = output_captures[i];
178 		if (output_type == ROutput::Output) {
179 			if (cap.mode & RecordOutput) appendToOutputList (&(cap.recorded), output, output_type);
180 			if (cap.mode & SuppressOutput) return previously_empty;
181 		} else {
182 			if (cap.mode & RecordMessages) appendToOutputList (&(cap.recorded), output, output_type);
183 			if (cap.mode & SuppressMessages) return previously_empty;
184 		}
185 	}
186 
187 	appendToOutputList (&output_buffer, output, output_type);
188 	out_buf_len += buf_length;
189 
190 	return previously_empty;
191 }
192 
flushOutput(bool forcibly)193 ROutputList RKROutputBuffer::flushOutput (bool forcibly) {
194 	ROutputList ret;
195 
196 	if (out_buf_len == 0) return ret;		// if there is absolutely no output, just skip.
197 	RK_TRACE (RBACKEND);
198 
199 	if (!forcibly) {
200 		if (!output_buffer_mutex.tryLock ()) return ret;
201 	} else {
202 		output_buffer_mutex.lock ();
203 	}
204 
205 	RK_ASSERT (!output_buffer.isEmpty ());	// see check for out_buf_len, above
206 
207 	ret = output_buffer;
208 	output_buffer.clear ();
209 	out_buf_len = 0;
210 
211 	output_buffer_mutex.unlock ();
212 	return ret;
213 }
214 
215 
216 
quote(const QString & string)217 QString RKRSharedFunctionality::quote (const QString &string) {
218 	QString ret;
219 	int s = string.size ();
220 	ret.reserve (s + 2);	// typical case: Only quotes added, no escapes needed.
221 	ret.append ('\"');
222 	for (int i = 0; i < s; ++i) {
223 		const QChar c = string[i];
224 		if ((c == '\\') || (c == '\"')) ret.append ('\\');
225 		ret.append (c);
226 	}
227 	ret.append ('\"');
228 
229 	return ret;
230 }
231 
232 #include <QFile>
233 /* These definitions don't really belong into this file, but, importantly, they need to be compiled for both frontend and backend. */
234 namespace RK_Debug {
235 	int RK_Debug_Level = 0;
236 	int RK_Debug_Flags = DEBUG_ALL;
237 	int RK_Debug_CommandStep = 0;
238 	QFile *debug_file = 0;
239 
setupLogFile(const QString & basename)240 	bool setupLogFile (const QString &basename) {
241 		QStringList all_debug_files (basename);
242 		all_debug_files << basename + ".0" << basename + ".1";
243 		for (int i = all_debug_files.size () -1; i >= 0; --i) {
244 			QFile oldfile (all_debug_files[i]);
245 			if (oldfile.exists ()) {
246 				if (i < all_debug_files.size () -1) {
247 					oldfile.rename (all_debug_files[i+1]);
248 				} else {
249 					oldfile.remove ();
250 				}
251 			}
252 		}
253 		debug_file = new QFile (basename);
254 		return (debug_file->open (QIODevice::WriteOnly | QIODevice::Truncate));
255 	}
256 };
257