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