]> git.lyx.org Git - lyx.git/blob - src/Server.cpp
Fix bug #6175: Footnotes in title not translated.
[lyx.git] / src / Server.cpp
1 /**
2  * \file Server.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author Enrico Forestieri
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 /**
16   Docu   : To use the lyxserver define the name of the pipe in your
17            lyxrc:
18            \serverpipe "/home/myhome/.lyxpipe"
19            Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
20            Each message consists of a single line in ASCII. Input lines
21            (client -> LyX) have the following format:
22             "LYXCMD:<clientname>:<functionname>:<argument>"
23            Answers from LyX look like this:
24            "INFO:<clientname>:<functionname>:<data>"
25  [asierra970531] Or like this in case of error:
26            "ERROR:<clientname>:<functionname>:<error message>"
27            where <clientname> and <functionname> are just echoed.
28            If LyX notifies about a user defined extension key-sequence,
29            the line looks like this:
30            "NOTIFY:<key-sequence>"
31  [asierra970531] New server-only messages to implement a simple protocol
32            "LYXSRV:<clientname>:<protocol message>"
33            where <protocol message> can be "hello" or "bye". If hello is
34            received LyX will inform the client that it's listening its
35            messages, and 'bye' will inform that lyx is closing.
36
37            See development/server_monitor.c for an example client.
38   Purpose: implement a client/server lib for LyX
39 */
40
41 #include <config.h>
42
43 #include "Server.h"
44 #include "FuncRequest.h"
45 #include "LyXAction.h"
46 #include "LyXFunc.h"
47
48 #include "frontends/Application.h"
49
50 #include "support/debug.h"
51 #include "support/FileName.h"
52 #include "support/lstrings.h"
53 #include "support/os.h"
54
55 #include <boost/bind.hpp>
56
57 #ifdef _WIN32
58 #include <QCoreApplication>
59 #endif
60
61 #include <cerrno>
62 #ifdef HAVE_SYS_STAT_H
63 # include <sys/stat.h>
64 #endif
65 #include <fcntl.h>
66
67 using namespace std;
68 using namespace lyx::support;
69 using os::external_path;
70
71 namespace lyx {
72
73 /////////////////////////////////////////////////////////////////////
74 //
75 // LyXComm
76 //
77 /////////////////////////////////////////////////////////////////////
78
79 #if defined(_WIN32)
80
81 class PipeEvent : public QEvent {
82 public:
83         ///
84         PipeEvent(HANDLE inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
85         {}
86         ///
87         HANDLE pipe() const { return inpipe_; }
88
89 private:
90         HANDLE inpipe_;
91 };
92
93 namespace {
94
95 bool closing_ = false;
96
97 void closing()
98 {
99         closing_ = true;
100 }
101
102
103 char * errormsg()
104 {
105         void * msgbuf;
106         DWORD error = GetLastError();
107         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
108                       FORMAT_MESSAGE_FROM_SYSTEM |
109                       FORMAT_MESSAGE_IGNORE_INSERTS,
110                       NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
111                       (LPTSTR) &msgbuf, 0, NULL);
112         return static_cast<char *>(msgbuf);
113 }
114
115
116 extern "C" {
117
118 DWORD WINAPI pipeServerWrapper(void * arg)
119 {
120         LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
121         lyxcomm->pipeServer();
122         return 1;
123 }
124
125 }
126
127 } // namespace anon
128
129 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
130         : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
131 {
132         // Ask Qt to notify us on quit.
133         qAddPostRoutine(closing);
134         ready_ = false;
135         openConnection();
136 }
137
138
139 void LyXComm::pipeServer()
140 {
141         int const bufsize = 1024;
142         HANDLE inpipe;
143
144         outpipe_ = CreateNamedPipe(external_path(outPipeName()).c_str(),
145                                    PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT,
146                                    PIPE_UNLIMITED_INSTANCES,
147                                    bufsize, 0, 0, NULL);
148         if (outpipe_ == INVALID_HANDLE_VALUE) {
149                 lyxerr << "LyXComm: Could not create pipe "
150                        << external_path(outPipeName()) << '\n'
151                        << errormsg() << endl;
152                 return;
153         }
154         ConnectNamedPipe(outpipe_, NULL);
155         // Now change to blocking mode
156         DWORD mode = PIPE_WAIT;
157         SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
158
159         while (!checkStopServerEvent()) {
160                 inpipe = CreateNamedPipe(external_path(inPipeName()).c_str(),
161                                          PIPE_ACCESS_INBOUND, PIPE_WAIT,
162                                          PIPE_UNLIMITED_INSTANCES,
163                                          0, bufsize, 0, NULL);
164                 if (inpipe == INVALID_HANDLE_VALUE) {
165                         lyxerr << "LyXComm: Could not create pipe "
166                                << external_path(inPipeName()) << '\n'
167                                << errormsg() << endl;
168                         break;
169                 }
170
171                 BOOL connected = ConnectNamedPipe(inpipe, NULL);
172                 // Check whether we are signaled to shutdown the pipe server.
173                 if (checkStopServerEvent()) {
174                         CloseHandle(inpipe);
175                         break;
176                 }
177                 if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
178                         PipeEvent * event = new PipeEvent(inpipe);
179                         QCoreApplication::postEvent(this,
180                                         static_cast<QEvent *>(event));
181                 } else
182                         CloseHandle(inpipe);
183         }
184         CloseHandle(outpipe_);
185 }
186
187
188 bool LyXComm::event(QEvent * e)
189 {
190         if (e->type() == QEvent::User) {
191                 read_ready(static_cast<PipeEvent *>(e)->pipe());
192                 return true;
193         }
194         return false;
195 }
196
197
198 BOOL LyXComm::checkStopServerEvent()
199 {
200         return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0 || closing_;
201 }
202
203
204 void LyXComm::openConnection()
205 {
206         LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
207
208         // If we are up, that's an error
209         if (ready_) {
210                 lyxerr << "LyXComm: Already connected" << endl;
211                 return;
212         }
213         // We assume that we don't make it
214         ready_ = false;
215
216         if (pipename_.empty()) {
217                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
218                 return;
219         }
220
221         // Check whether the pipe is being used by some other program.
222         if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
223                 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
224                        << " already exists.\nMaybe another instance of LyX"
225                           " is using it."
226                        << endl;
227                 pipename_.erase();
228                 return;
229         }
230
231         stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
232         DWORD tid;
233         HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
234                                      static_cast<void *>(this), 0, &tid);
235         if (!thread) {
236                 lyxerr << "LyXComm: Could not create pipe server thread: "
237                        << errormsg() << endl;
238                 return;
239         } else
240                 CloseHandle(thread);
241
242         // We made it!
243         ready_ = true;
244         LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
245 }
246
247
248 /// Close pipes
249 void LyXComm::closeConnection()
250 {
251         LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
252
253         if (pipename_.empty()) {
254                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
255                 return;
256         }
257
258         if (!ready_) {
259                 LYXERR0("LyXComm: Already disconnected");
260                 return;
261         }
262
263         SetEvent(stopserver_);
264         HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
265                                   GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
266                                   FILE_ATTRIBUTE_NORMAL, NULL);
267         if (hpipe != INVALID_HANDLE_VALUE)
268                 CloseHandle(hpipe);
269
270         ResetEvent(stopserver_);
271         CloseHandle(stopserver_);
272         ready_ = false;
273 }
274
275
276 void LyXComm::emergencyCleanup()
277 {
278         if (!pipename_.empty()) {
279                 SetEvent(stopserver_);
280                 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
281                                           GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
282                                           FILE_ATTRIBUTE_NORMAL, NULL);
283                 if (hpipe != INVALID_HANDLE_VALUE)
284                         CloseHandle(hpipe);
285
286                 ResetEvent(stopserver_);
287                 CloseHandle(stopserver_);
288         }
289 }
290
291 void LyXComm::read_ready(HANDLE inpipe)
292 {
293         string read_buffer_;
294         read_buffer_.erase();
295
296         int const charbuf_size = 100;
297         char charbuf[charbuf_size];
298
299         DWORD status;
300         while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
301                 if (status > 0) {
302                         charbuf[status] = '\0'; // turn it into a c string
303                         read_buffer_ += rtrim(charbuf, "\r");
304                         // commit any commands read
305                         while (read_buffer_.find('\n') != string::npos) {
306                                 // split() grabs the entire string if
307                                 // the delim /wasn't/ found. ?:-P
308                                 string cmd;
309                                 read_buffer_= split(read_buffer_, cmd, '\n');
310                                 cmd = rtrim(cmd, "\r");
311                                 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
312                                         << ", read_buffer_:" << read_buffer_
313                                         << ", cmd:" << cmd);
314                                 if (!cmd.empty())
315                                         clientcb_(client_, cmd);
316                                         //\n or not \n?
317                         }
318                 }
319         }
320         if (GetLastError() != ERROR_BROKEN_PIPE) {
321                 // An error occurred
322                 LYXERR0("LyXComm: " << errormsg());
323                 if (!read_buffer_.empty()) {
324                         LYXERR0("LyXComm: truncated command: " << read_buffer_);
325                         read_buffer_.erase();
326                 }
327         }
328         // Client closed the pipe, so disconnect and close.
329         DisconnectNamedPipe(inpipe);
330         CloseHandle(inpipe);
331         FlushFileBuffers(outpipe_);
332         DisconnectNamedPipe(outpipe_);
333         // Temporarily change to non-blocking mode otherwise
334         // ConnectNamedPipe() would block waiting for a connection.
335         DWORD mode = PIPE_NOWAIT;
336         SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
337         ConnectNamedPipe(outpipe_, NULL);
338         mode = PIPE_WAIT;
339         SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
340 }
341
342
343 void LyXComm::send(string const & msg)
344 {
345         if (msg.empty()) {
346                 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
347                 return;
348         }
349
350         LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
351
352         if (pipename_.empty()) return;
353
354         if (!ready_) {
355                 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
356                 return;
357         }
358
359         bool success;
360         int count = 0;
361         do {
362                 DWORD sent;
363                 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
364                 if (!success) {
365                         DWORD error = GetLastError();
366                         if (error == ERROR_NO_DATA) {
367                                 DisconnectNamedPipe(outpipe_);
368                                 DWORD mode = PIPE_NOWAIT;
369                                 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
370                                 ConnectNamedPipe(outpipe_, NULL);
371                                 mode = PIPE_WAIT;
372                                 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
373                         } else if (error != ERROR_PIPE_LISTENING)
374                                 break;
375                 }
376                 Sleep(100);
377                 ++count;
378         } while (!success && count < 100 && !checkStopServerEvent());
379
380         if (!success) {
381                 lyxerr << "LyXComm: Error sending message: " << msg
382                        << '\n' << errormsg()
383                        << "\nLyXComm: Resetting connection" << endl;
384                 closeConnection();
385                 if (!checkStopServerEvent())
386                         openConnection();
387         }
388 }
389
390
391 #elif !defined (HAVE_MKFIFO)
392 // We provide a stub class that disables the lyxserver.
393
394 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
395 {}
396
397 void LyXComm::openConnection()
398 {}
399
400
401 void LyXComm::closeConnection()
402 {}
403
404
405 int LyXComm::startPipe(string const & filename, bool write)
406 {
407         return -1;
408 }
409
410
411 void LyXComm::endPipe(int & fd, string const & filename, bool write)
412 {}
413
414
415 void LyXComm::emergencyCleanup()
416 {}
417
418 void LyXComm::read_ready()
419 {}
420
421
422 void LyXComm::send(string const & msg)
423 {}
424
425
426 #else // defined (HAVE_MKFIFO)
427
428
429 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
430         : pipename_(pip), client_(cli), clientcb_(ccb)
431 {
432         ready_ = false;
433         openConnection();
434 }
435
436
437 void LyXComm::openConnection()
438 {
439         LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
440
441         // If we are up, that's an error
442         if (ready_) {
443                 lyxerr << "LyXComm: Already connected" << endl;
444                 return;
445         }
446         // We assume that we don't make it
447         ready_ = false;
448
449         if (pipename_.empty()) {
450                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
451                 return;
452         }
453
454         infd_ = startPipe(inPipeName(), false);
455         if (infd_ == -1)
456                 return;
457
458         outfd_ = startPipe(outPipeName(), true);
459         if (outfd_ == -1) {
460                 endPipe(infd_, inPipeName(), false);
461                 return;
462         }
463
464         if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
465                 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
466                        << '\n' << strerror(errno) << endl;
467                 return;
468         }
469
470         // We made it!
471         ready_ = true;
472         LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
473 }
474
475
476 /// Close pipes
477 void LyXComm::closeConnection()
478 {
479         LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
480
481         if (pipename_.empty()) {
482                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
483                 return;
484         }
485
486         if (!ready_) {
487                 LYXERR0("LyXComm: Already disconnected");
488                 return;
489         }
490
491         endPipe(infd_, inPipeName(), false);
492         endPipe(outfd_, outPipeName(), true);
493
494         ready_ = false;
495 }
496
497
498 int LyXComm::startPipe(string const & file, bool write)
499 {
500         static bool stalepipe = false;
501         FileName const filename(file);
502         if (filename.exists()) {
503                 if (!write) {
504                         // Let's see whether we have a stale pipe.
505                         int fd = ::open(filename.toFilesystemEncoding().c_str(),
506                                         O_WRONLY | O_NONBLOCK);
507                         if (fd >= 0) {
508                                 // Another LyX instance is using it.
509                                 ::close(fd);
510                         } else if (errno == ENXIO) {
511                                 // No process is reading from the other end.
512                                 stalepipe = true;
513                                 LYXERR(Debug::LYXSERVER,
514                                         "LyXComm: trying to remove "
515                                         << filename);
516                                 filename.removeFile();
517                         }
518                 } else if (stalepipe) {
519                         LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
520                                 << filename);
521                         filename.removeFile();
522                         stalepipe = false;
523                 }
524                 if (filename.exists()) {
525                         lyxerr << "LyXComm: Pipe " << filename
526                                << " already exists.\nIf no other LyX program"
527                                   " is active, please delete the pipe by hand"
528                                   " and try again."
529                                << endl;
530                         pipename_.erase();
531                         return -1;
532                 }
533         }
534
535         if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
536                 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
537                        << strerror(errno) << endl;
538                 return -1;
539         }
540         int const fd = ::open(filename.toFilesystemEncoding().c_str(),
541                               write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
542
543         if (fd < 0) {
544                 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
545                        << strerror(errno) << endl;
546                 filename.removeFile();
547                 return -1;
548         }
549
550         if (!write) {
551                 theApp()->registerSocketCallback(fd,
552                         boost::bind(&LyXComm::read_ready, this));
553         }
554
555         return fd;
556 }
557
558
559 void LyXComm::endPipe(int & fd, string const & filename, bool write)
560 {
561         if (fd < 0)
562                 return;
563
564         if (!write)
565                 theApp()->unregisterSocketCallback(fd);
566
567         if (::close(fd) < 0) {
568                 lyxerr << "LyXComm: Could not close pipe " << filename
569                        << '\n' << strerror(errno) << endl;
570         }
571
572         if (FileName(filename).removeFile() < 0) {
573                 lyxerr << "LyXComm: Could not remove pipe " << filename
574                        << '\n' << strerror(errno) << endl;
575         }
576
577         fd = -1;
578 }
579
580
581 void LyXComm::emergencyCleanup()
582 {
583         if (!pipename_.empty()) {
584                 endPipe(infd_, inPipeName(), false);
585                 endPipe(outfd_, outPipeName(), true);
586         }
587 }
588
589
590 // Receives messages and sends then to client
591 void LyXComm::read_ready()
592 {
593         // FIXME: make read_buffer_ a class-member for multiple sessions
594         static string read_buffer_;
595         read_buffer_.erase();
596
597         int const charbuf_size = 100;
598         char charbuf[charbuf_size];
599
600         // As O_NONBLOCK is set, until no data is available for reading,
601         // read() doesn't block but returns -1 and set errno to EAGAIN.
602         // After a client that opened the pipe for writing, closes it
603         // (and no other client is using the pipe), read() would always
604         // return 0 and thus the connection has to be reset.
605
606         errno = 0;
607         int status;
608         // the single = is intended here.
609         while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
610
611                 if (status > 0) {
612                         charbuf[status] = '\0'; // turn it into a c string
613                         read_buffer_ += rtrim(charbuf, "\r");
614                         // commit any commands read
615                         while (read_buffer_.find('\n') != string::npos) {
616                                 // split() grabs the entire string if
617                                 // the delim /wasn't/ found. ?:-P
618                                 string cmd;
619                                 read_buffer_= split(read_buffer_, cmd,'\n');
620                                 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
621                                         << ", read_buffer_:" << read_buffer_
622                                         << ", cmd:" << cmd);
623                                 if (!cmd.empty())
624                                         clientcb_(client_, cmd);
625                                         //\n or not \n?
626                         }
627                 } else {
628                         if (errno == EAGAIN) {
629                                 // Nothing to read, continue
630                                 errno = 0;
631                                 return;
632                         }
633                         // An error occurred, better bailing out
634                         LYXERR0("LyXComm: " << strerror(errno));
635                         if (!read_buffer_.empty()) {
636                                 LYXERR0("LyXComm: truncated command: " << read_buffer_);
637                                 read_buffer_.erase();
638                         }
639                         break; // reset connection
640                 }
641         }
642
643         // The connection gets reset when read() returns 0 (meaning that the
644         // last client closed the pipe) or an error occurred, in which case
645         // read() returns -1 and errno != EAGAIN.
646         closeConnection();
647         openConnection();
648         errno = 0;
649 }
650
651
652 void LyXComm::send(string const & msg)
653 {
654         if (msg.empty()) {
655                 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
656                 return;
657         }
658
659         LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
660
661         if (pipename_.empty()) return;
662
663         if (!ready_) {
664                 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
665         } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
666                 lyxerr << "LyXComm: Error sending message: " << msg
667                        << '\n' << strerror(errno)
668                        << "\nLyXComm: Resetting connection" << endl;
669                 closeConnection();
670                 openConnection();
671         }
672 }
673
674 #endif // defined (HAVE_MKFIFO)
675
676
677 string const LyXComm::inPipeName() const
678 {
679         return pipename_ + ".in";
680 }
681
682
683 string const LyXComm::outPipeName() const
684 {
685         return pipename_ + ".out";
686 }
687
688
689 /////////////////////////////////////////////////////////////////////
690 //
691 // Server
692 //
693 /////////////////////////////////////////////////////////////////////
694
695 void ServerCallback(Server * server, string const & msg)
696 {
697         server->callback(msg);
698 }
699
700 Server::Server(LyXFunc * f, string const & pipes)
701         : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
702 {}
703
704
705 Server::~Server()
706 {
707         // say goodbye to clients so they stop sending messages
708         // send as many bye messages as there are clients,
709         // each with client's name.
710         string message;
711         for (int i = 0; i != numclients_; ++i) {
712                 message = "LYXSRV:" + clients_[i] + ":bye\n";
713                 pipes_.send(message);
714         }
715 }
716
717
718 int compare(char const * a, char const * b, unsigned int len)
719 {
720         using namespace std;
721         return strncmp(a, b, len);
722 }
723
724
725 // Handle data gotten from communication, called by LyXComm
726 void Server::callback(string const & msg)
727 {
728         LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
729
730         char const * p = msg.c_str();
731
732         // --- parse the string --------------------------------------------
733         //
734         //  Format: LYXCMD:<client>:<func>:<argstring>\n
735         //
736         bool server_only = false;
737         while (*p) {
738                 // --- 1. check 'header' ---
739
740                 if (compare(p, "LYXSRV:", 7) == 0) {
741                         server_only = true;
742                 } else if (0 != compare(p, "LYXCMD:", 7)) {
743                         lyxerr << "Server: Unknown request \""
744                                << p << '"' << endl;
745                         return;
746                 }
747                 p += 7;
748
749                 // --- 2. for the moment ignore the client name ---
750                 string client;
751                 while (*p && *p != ':')
752                         client += char(*p++);
753                 if (*p == ':')
754                         ++p;
755                 if (!*p)
756                         return;
757
758                 // --- 3. get function name ---
759                 string cmd;
760                 while (*p && *p != ':')
761                         cmd += char(*p++);
762
763                 // --- 4. parse the argument ---
764                 string arg;
765                 if (!server_only && *p == ':' && *(++p)) {
766                         while (*p && *p != '\n')
767                                 arg += char(*p++);
768                         if (*p) ++p;
769                 }
770
771                 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
772                         << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
773
774                 // --- lookup and exec the command ------------------
775
776                 if (server_only) {
777                         string buf;
778                         // return the greeting to inform the client that
779                         // we are listening.
780                         if (cmd == "hello") {
781                                 // One more client
782                                 if (numclients_ == MAX_CLIENTS) { //paranoid check
783                                         LYXERR(Debug::LYXSERVER, "Server: too many clients...");
784                                         return;
785                                 }
786                                 int i = 0;
787                                 while (!clients_[i].empty() && i < numclients_)
788                                         ++i;
789                                 clients_[i] = client;
790                                 ++numclients_;
791                                 buf = "LYXSRV:" + client + ":hello\n";
792                                 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
793                                 pipes_.send(buf);
794                         } else if (cmd == "bye") {
795                                 // If clients_ == 0 maybe we should reset the pipes
796                                 // to prevent fake callbacks
797                                 int i = 0; //look if client is registered
798                                 for (; i < numclients_; ++i) {
799                                         if (clients_[i] == client)
800                                                 break;
801                                 }
802                                 if (i < numclients_) {
803                                         --numclients_;
804                                         clients_[i].erase();
805                                         LYXERR(Debug::LYXSERVER, "Server: Client "
806                                                 << client << " said goodbye");
807                                 } else {
808                                         LYXERR(Debug::LYXSERVER,
809                                                 "Server: ignoring bye messge from unregistered client" << client);
810                                 }
811                         } else {
812                                 LYXERR0("Server: Undefined server command " << cmd << '.');
813                         }
814                         return;
815                 }
816
817                 if (!cmd.empty()) {
818                         // which lyxfunc should we let it connect to?
819                         // The correct solution would be to have a
820                         // specialized (non-gui) BufferView. But how do
821                         // we do it now? Probably we should just let it
822                         // connect to the lyxfunc in the single LyXView we
823                         // support currently. (Lgb)
824
825                         func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
826                         string const rval = to_utf8(func_->getMessage());
827
828                         // all commands produce an INFO or ERROR message
829                         // in the output pipe, even if they do not return
830                         // anything. See chapter 4 of Customization doc.
831                         string buf;
832                         if (func_->errorStat())
833                                 buf = "ERROR:";
834                         else
835                                 buf = "INFO:";
836                         buf += client + ':' + cmd + ':' +  rval + '\n';
837                         pipes_.send(buf);
838
839                         // !!! we don't do any error checking -
840                         //  if the client won't listen, the
841                         //  message is lost and others too
842                         //  maybe; so the client should empty
843                         //  the outpipe before issuing a request.
844
845                         // not found
846                 }
847         }  // while *p
848 }
849
850
851 // Send a notify message to a client, called by WorkAreaKeyPress
852 void Server::notifyClient(string const & s)
853 {
854         pipes_.send("NOTIFY:" + s + "\n");
855 }
856
857
858 } // namespace lyx
859
860 #ifdef _WIN32
861 #include "moc_Server.cpp"
862 #endif