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