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