summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPixel <Pixel>2001-09-20 23:27:01 +0000
committerPixel <Pixel>2001-09-20 23:27:01 +0000
commit8346d0774d2d1e076038db27f65f1d082a460f16 (patch)
tree132f84cf1ef45d5006a2b1d52d4d40b1e8e51abc /lib
Initial revision
Diffstat (limited to 'lib')
-rw-r--r--lib/Action.cc87
-rw-r--r--lib/Confirm.cc26
-rw-r--r--lib/Exceptions.cc88
-rw-r--r--lib/Form.cc56
-rw-r--r--lib/Handle.cc110
-rw-r--r--lib/HttpServ.cc297
-rw-r--r--lib/Input.cc32
-rw-r--r--lib/Makefile.am12
-rw-r--r--lib/Menu.cc26
-rw-r--r--lib/Message.cc21
-rw-r--r--lib/Output.cc32
-rw-r--r--lib/Socket.cc140
-rw-r--r--lib/String.cc291
-rw-r--r--lib/Table.cc41
-rw-r--r--lib/Variables.cc55
-rw-r--r--lib/checkargs.c85
-rw-r--r--lib/datecalc.c84
17 files changed, 1483 insertions, 0 deletions
diff --git a/lib/Action.cc b/lib/Action.cc
new file mode 100644
index 0000000..292e669
--- /dev/null
+++ b/lib/Action.cc
@@ -0,0 +1,87 @@
+#include "String.h"
+#include "Action.h"
+#include "HttpServ.h"
+#include "config.h"
+
+Action * Action::start = 0;
+
+static int counter = 0;
+
+static String genurl(const String & u) {
+ if (u.strlen()) {
+ return u;
+ } else {
+ // Si l'url passée en paramètre est vide, on génère une URL
+ // sous la forme TmpXXXX où XXXX est une valeur qui s'incrémente.
+ return String("Tmp") + (counter++);
+ }
+}
+
+Action::Action(const String & u) : next(start), prev(0), URL(genurl(u)), hastoclean(false), accessed(false) {
+ start = this;
+ if (next) next->prev = this;
+}
+
+Action::~Action() {
+ if (start == this) {
+ start = next;
+ }
+ if (next) next->prev = prev;
+ if (prev) prev->next = next;
+}
+
+Action * Action::Look4URL(const String & URL) {
+ Action * p;
+
+ for (p = start; p; p = p->next) {
+ if (!p->GetURL().strlen()) {
+ // Si l'action que l'on vient de tester n'a pas d'URL, elle ne sert à rien.
+ // On l'efface donc de notre liste. GetURL renvoie une chaine vide si l'action
+ // était déclarée en mode CleanUp, puis a été accédée.
+ delete p;
+ } else {
+ if (URL == p->GetURL()) return p;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Voici la skin principale. Elle nécessite un fichier 'grain.png' dans le répertoire datas
+ * et un fichier 'style.css' (fournis)
+ */
+
+void Action::SendHead(Handle * h) {
+ (*h) << "<HTML><HEAD><TITLE>" << GetTitle() << "</TITLE>" << endnl <<
+ "<META http-equiv=\"Content-Style-Type\" content=\"text/css\">" << endnl <<
+ "<LINK REL=STYLESHEET HREF=\"/image/style.css\" TYPE=\"text/css\">" << endnl <<
+ "</HEAD><BODY BGCOLOR=\"#aaaaaa\" BACKGROUND=\"/image/grain.png\">" << endnl <<
+ "<center><TABLE WIDTH=\"50%\" BORDER=3 cellspacing=1 BGCOLOR=\"#ffffff\"><TR><TD>" << endnl <<
+ "<center><b><h1>" << GetTitle() << "</h1></b></center></TD></TR></TABLE><P>" << endnl <<
+ "<TABLE WIDTH=\"80%\" BORDER=2 cellspacing=1 BGCOLOR=\"#ffffff\"><TR><TD>" << endnl;
+}
+
+void Action::SendFoot(Handle * h) {
+ (*h) << "</TABLE></TD></TR></BODY></HTML>" << endnl;
+}
+
+String Action::GetURL(void) {
+ // Comme décrit plus haut, il faut renvoyer la chaîne vide si l'action est en
+ // mode CleanUp et a été lue.
+ return (hastoclean && accessed) ? "" : URL;
+}
+
+void Action::CleanUp(void) {
+ hastoclean = true;
+}
+
+void Action::Accessed(void) {
+ accessed = true;
+}
+
+void Action::ShowButton(Handle * h, const String & l, const String & u) {
+ (*h) << "<CENTER><FORM METHOD=\"POST\" ACTION=\"/bin/" << u << "\">" << endnl <<
+ "<INPUT TYPE=\"SUBMIT\" VALUE=\"" << l << "\">" << endnl
+ << "</FORM></CENTER>" << endnl;
+}
diff --git a/lib/Confirm.cc b/lib/Confirm.cc
new file mode 100644
index 0000000..5177f8b
--- /dev/null
+++ b/lib/Confirm.cc
@@ -0,0 +1,26 @@
+#include "HttpServ.h"
+#include "Confirm.h"
+#include "config.h"
+
+Confirm::Confirm(const String & t, const String & m, const String & U, Action * y, Action * n) :
+ Action(U), tit(t), msg(m), NYes(y), NNo(n) { }
+
+void Confirm::Do(Variables * v, Handle * h) {
+ SendHead(h);
+ (*h) << msg << "<CENTER><TABLE BORDER=0><tr><td>" << endnl <<
+ "<FORM METHOD=\"POST\" ACTION=\"/bin/" << (NYes ? NYes->GetURL() : "start") << "\">" << endnl <<
+ "<INPUT TYPE=\"SUBMIT\" VALUE=\" Oui \">" << endnl;
+ v->Dump(h);
+ (*h) << "</FORM></td><td>" << endnl <<
+ "<FORM METHOD=\"POST\" ACTION=\"/bin/" << (NNo ? NNo->GetURL() : "start") << "\">" << endnl <<
+ "<INPUT TYPE=\"SUBMIT\" VALUE=\" Non \">" << endnl;
+ v->Dump(h);
+ (*h) << "</FORM></td></tr></TABLE></CENTER>" << endnl;
+ SendFoot(h);
+
+ Accessed();
+}
+
+String Confirm::GetTitle(void) {
+ return tit;
+}
diff --git a/lib/Exceptions.cc b/lib/Exceptions.cc
new file mode 100644
index 0000000..b5a33bd
--- /dev/null
+++ b/lib/Exceptions.cc
@@ -0,0 +1,88 @@
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include "config.h"
+#include "String.h"
+#include "Exceptions.h"
+
+char GeneralException::t[BUFSIZ];
+
+char * Base::strdup(const char * s) const {
+ return xstrdup(s);
+}
+
+void * Base::malloc(ssize_t s) const {
+ return xmalloc(s);
+}
+
+/*
+void * Base::operator new(size_t s) {
+ return memset(xmalloc(s), 0, s);
+}
+
+void * Base::operator new(size_t s, void * & p) {
+ return memset(p = xmalloc(s), 0, s);
+}
+*/
+
+GeneralException::GeneralException(String emsg) : msg(strdup(emsg.to_charp())) { }
+GeneralException::GeneralException() : msg(0) { }
+GeneralException::GeneralException(const GeneralException & e) : msg(strdup(e.msg)) { }
+
+GeneralException::~GeneralException() {
+ free(msg);
+}
+
+char * GeneralException::GetMsg() {
+ return msg;
+}
+
+MemoryException::MemoryException(ssize_t s) {
+ sprintf(t, _("Failed allocating %lld bytes."), s);
+ msg = strdup(t);
+}
+
+IOException::IOException(String fn, op_t op, ssize_t s) {
+ sprintf(t, _("An error has occured while %s %lld bytes from %s: %s"), op == IO_WRITE ? _("writing") : _("reading"),
+ s, fn.to_charp(), strerror(errno));
+ msg = strdup(t);
+}
+
+IOInternal::IOInternal(String fn, op_t op) {
+ sprintf(t, _("Internal error: has occured while %s from %s: open for %s."), op == IO_WRITE ? _("writing") : _("reading"),
+ fn.to_charp(), op == IO_WRITE ? _("reading") : _("writing"));
+ msg = strdup(t);
+}
+
+IOGeneral::IOGeneral(String fn) : GeneralException(fn) { }
+
+char * xstrdup(const char * s) throw (MemoryException) {
+ char * r;
+
+ if (!(r = ::strdup(s))) {
+ throw MemoryException(strlen(s + 1));
+ }
+
+ return r;
+}
+
+void * xmalloc(ssize_t s) throw (MemoryException) {
+ void * r;
+
+ if (!(r = ::malloc(s))) {
+ throw MemoryException(s);
+ }
+
+ return r;
+}
+
+#undef free
+
+void xfree(void *& p) {
+ if (p) {
+ ::free(p);
+ p = 0;
+ }
+}
diff --git a/lib/Form.cc b/lib/Form.cc
new file mode 100644
index 0000000..5fb885a
--- /dev/null
+++ b/lib/Form.cc
@@ -0,0 +1,56 @@
+#include "Form.h"
+#include "HttpServ.h"
+
+Form::Form(const String & titre, const String & url, const String & inv, String * names, String * invs,
+ String * defaults, String ** lists, String ** descs, int nb, Action * na) :
+ Action(url), tit(titre), iv(inv), nms(names), ivs(invs), defs(defaults), lsts(lists), dscs(descs), n(nb), Next(na) { }
+
+String Form::GetTitle(void) {
+ return tit;
+}
+
+void Form::Do(Variables * v, Handle * h) {
+ SendHead(h);
+
+ (*h) <<
+"<center>" << endnl <<
+"<form ACTION=\"/bin/" << (Next ? Next->GetURL() : "start") << "\" METHOD=POST>" << endnl;
+
+ v->Dump(h);
+
+ (*h) <<
+"<table BORDER=0 CELLSPACING=0 BGCOLOR=\"#000000\"><tr><td>" << endnl <<
+"<table BORDER=0 CELLSPACING=0 CELLPADDING=3 WIDTH=\"300\" BGCOLOR=\"#FFFFCC\">" << endnl <<
+"<tr><td ALIGN=CENTER WIDTH=\"100%\" BGCOLOR=\"#000000\"><b><font FACE=\"arial,helvetica\" COLOR=\"#FFFFFF\">" << endnl <<
+iv << endnl <<
+"</font></b></td></tr>" << endnl;
+
+ for (int i = 0; i < n; i++) {
+ (*h) <<
+"<tr><td>" << ivs[i] << "</td></tr>" << endnl;
+ if (lsts[i]) {
+ String * s, * t;
+ (*h) <<
+"<tr><td><select NAME=\"" << nms[i] << "\">" << endnl;
+ for (s = lsts[i], t = dscs[i]; s->strlen(); s++, t++) {
+ (*h) <<
+"<option VALUE=\"" << *s << "\">" << *t << "</option>" << endnl;
+ }
+ (*h) <<
+"</select></td></tr>" << endnl;
+ } else {
+ (*h) <<
+"<tr><td><input TYPE=\"text\" NAME=\"" << nms[i] << "\" VALUE=\"" << defs[i] << "\" size=40></tr></td>" << endnl;
+ }
+ }
+
+ (*h) <<
+
+"<tr><td ALIGN=\"center\"><input TYPE=\"submit\" VALUE=\" Ok \"></td></tr>" << endnl <<
+"</table></td></tr></table></form></center>" << endnl;
+
+ SendFoot(h);
+
+ Accessed();
+}
+
diff --git a/lib/Handle.cc b/lib/Handle.cc
new file mode 100644
index 0000000..8108285
--- /dev/null
+++ b/lib/Handle.cc
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "Handle.h"
+#include "config.h"
+
+Handle::Handle(const Handle & nh) : h(dup(nh.h)), closed(false) { }
+
+Handle::~Handle() {
+ if (h != -1) {
+ ::close(h);
+ }
+}
+
+Handle::Handle(int nh) : h(nh), closed(false) { }
+
+int Handle::GetHandle() {
+ return h;
+}
+
+ssize_t Handle::write(const void *buf, size_t count) throw (IOException) {
+ ssize_t r, tr = 0;
+ bool done, full = false;
+
+ do {
+ done = true;
+ if ((r = ::write(h, buf, count)) < 0) {
+ if ((!errno) || (errno = EAGAIN)) {
+ // Avant de déclarer une erreur, on vérifie si ce n'est pas un
+ // problème lié au fait qu'il n'y a plus de place libre. Cela peut
+ // arriver si l'on agit sur un pipe ou un handle. Nous
+ // attendons encore une fois avant de déclarer l'erreur,
+ // grace au drapeau full.
+ if (full) {
+ throw IOException(GetName(), IO_WRITE, count);
+ } else {
+ sleep(1);
+ done = false;
+ full = true;
+ tr += r;
+ }
+ } else {
+ throw IOException(GetName(), IO_WRITE, count);
+ }
+ } else if (r != count) {
+ full = done = false;
+ ((char *)buf) += r;
+ tr += r;
+ }
+ } while (!done);
+
+ return r + tr;
+}
+
+ssize_t Handle::read(void *buf, size_t count) throw (IOException) {
+ ssize_t r;
+
+ if ((r = ::read(h, buf, count)) < 0) {
+ if ((!errno) || (errno = EAGAIN)) {
+ // Avant de déclarer une erreur, on vérifie si ce n'est pas un
+ // problème lié au fait qu'il n'y a plus d'octets.
+ return 0;
+ } else {
+ throw IOException(GetName(), IO_READ, count);
+ }
+ }
+
+ if (!r) {
+ closed = true;
+ }
+
+ return r;
+}
+
+bool Handle::IsClosed(void) {
+ return closed;
+}
+
+Handle & operator<<(Handle & h, const String & s) {
+ char * p;
+
+ p = s.to_charp();
+ h.write(p, strlen(p));
+
+ return h;
+}
+
+Handle & operator>>(Handle & h, String & s) {
+ char t[BUFSIZ];
+ int i = 0, r;
+
+ while ((r = h.read(&(t[i]), 1)) && (i != (BUFSIZ - 1))) {
+ // Il y a souvent des \r\n dans les sockets par exemple.
+ // On ignore le \r pour ne garder que le \n, standard sous
+ // Unix.
+ if (t[i] == '\r') {
+ continue;
+ }
+ if (t[i] == '\n') {
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ t[i] = '\0';
+ s.set("%s", t);
+ return h;
+}
diff --git a/lib/HttpServ.cc b/lib/HttpServ.cc
new file mode 100644
index 0000000..dfead28
--- /dev/null
+++ b/lib/HttpServ.cc
@@ -0,0 +1,297 @@
+#include "Action.h"
+#include "HttpServ.h"
+#include "Socket.h"
+#include "config.h"
+
+String endhl = "\r\n", endnl = "\n";
+
+HttpServ::HttpServ(int port, const String & nname) : name(nname), localport(port) {
+ Listener.SetLocal("", port);
+ Listener.Listen();
+ cerr << "Mini HTTP-Server '" << name << "' ready and listening for port " << port << endl;
+}
+
+void HttpServ::MainLoop(Action * p) {
+ while (1) {
+ ProcessRequest(p);
+ }
+}
+
+void HttpServ::ProcessRequest(Action * p) {
+ Socket s(Listener.Accept());
+ String file, domain, t;
+ Action * f;
+ int len;
+
+ bad = false;
+
+ if (!s.IsConnected()) return;
+
+ cerr << "Got a request\n----\n";
+
+ bool post = ParseUri(file, domain, s);
+
+
+ len = -1;
+ do {
+ s >> t;
+ cerr << t << endl;
+ if ((t.strstr("Content-Length: ") == 0) || (t.strstr("Content-length: ") == 0)) {
+ cerr << "Saw 'Content-Lenght:', reading length from '" << t.to_charp(16) << "'\n";
+ len = String(t.to_charp(16)).to_int();
+ }
+ } while (t.strlen());
+
+ if (post) {
+ // On a pas eu de ligne 'Content-Length' mais on a eu une méthode POST.
+ // Cela est une erreur.
+ if (len == -1) {
+ bad = true;
+ Vars = new Variables(0);
+ } else {
+ cerr << "Got a POST request. Parsing variables. (len = " << len << ")\n";
+ // Les variables seront initialisées ici.
+ ParseVars(s, len);
+ }
+ } else {
+ Vars = new Variables(0);
+ }
+
+ cerr << " Domain = '" << domain << "' - File = '" << file << "'\n";
+
+ if (!bad) {
+ // Nous vérifions le domaine.
+ if (domain != "") {
+ bad = true;
+ // Les domaines valides sont '/', '/bin' et '/image'.
+ if (domain == "/image") bad = false;
+ if (domain == "/bin") bad = false;
+ if (domain == "/") bad = false;
+ } else {
+ // L'url sans domaine ni fichier est valide. (cela arrive sur certains navigateurs...)
+ bad = (file != "");
+ }
+ }
+
+ if (bad) {
+ ShowError(s);
+ } else {
+ if (((domain == "") || (domain == "/")) && (file == "")) {
+ // Si le navigateur a demandé l'URL '/', alors on renvoie une notification
+ // de redirection.
+ SendRedirect(s);
+ } else if (domain == "/bin") {
+ // Le domaine 'bin' est réservé aux actions. On cherche donc l'action à effectuer.
+ if ((f = p->Look4URL(file))) {
+ SendHeads(s, "text/html");
+ f->Do(Vars, &s);
+ } else {
+ ShowError(s);
+ }
+ } else {
+ // Dans tous les autres cas de domaine, on cherche le fichier dans le répertoire datas.
+ // On utilise try au cas où le fichier n'existe pas et donc que le constructeur
+ // d'input renvoie une erreur.
+ try {
+ Input i(String("datas/") + file);
+ SendHeads(s, GetMime(file));
+ s.ReadFile(i);
+ cerr << "File found, dumped.\n";
+ }
+ catch (IOGeneral e) {
+ ShowError(s);
+ cerr << "File not found, error showed.\n";
+ }
+ }
+ }
+
+ delete Vars;
+
+ cerr << "----\n";
+}
+
+void HttpServ::ParseVars(Socket & s, int len) {
+ String t, v;
+ char conv[3], l;
+ int hconv, nbvars;
+ ssize_t pos = 0, next;
+
+ t = "";
+ for (int i = 0; i < len; i++) {
+ s.read(&l, 1);
+ t += l;
+ }
+ cerr << "Post variables line: '" <<t << "'\n";
+
+
+ // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc le nombre d'occurences
+ // du caractère '=' indique le nombre de variables.
+ nbvars = t.strchrcnt('=');
+ Vars = new Variables(nbvars);
+
+ for (int i = 0; i < nbvars; i++) {
+ // Les variables sont sous la forme 'var1=val1&var2=val2&val3=var3'. Donc on cherche le caractère
+ // & dans la chaine POST.
+ next = t.strchr('&', pos);
+ if (next < 0) next = t.strlen();
+ v = "";
+ while (pos != next) {
+ switch (t[pos]) {
+ // Le navigateur encode les caractères spéciaux à l'aide du format %XX où XX indique
+ // la valeur hexadécimale du caractère. Nous encodons surtout les caractères
+ // ' ', '=', '%', et '/' avec cette technique.
+ case '%':
+ pos++;
+ conv[0] = t[pos++];
+ conv[1] = t[pos++];
+ conv[2] = '\0';
+ sscanf(conv, "%x", &hconv);
+ v += ((char) hconv);
+ break;
+ // Certains navigateurs utilisent '+' pour indiquer ' ' (qui est illégal) au lieu
+ // d'utiliser %20.
+ case '+':
+ v += ' ';
+ pos++;
+ break;
+ default:
+ v += t[pos++];
+ }
+ Vars->SetTo(i, v);
+ }
+ pos++;
+ }
+}
+
+/*
+ * Cette fonction renverra true si la méthode est une méthode POST.
+ * Les Strings domain et file seront modifiées afin de renvoyer le domaine
+ * et le fichier lut. La string s doit donner la première ligne de la requète,
+ * c'est à dire la méthode demandée par le client.
+ */
+
+bool HttpServ::ParseUri(String & file, String & domain, Socket & s) {
+ String t, Uri;
+ bool post = false;
+ char * p;
+ ssize_t sppos;
+
+ s >> t;
+ cerr << t << endl;
+
+ bad = false;
+
+ // p nous indiquera la position de la chaîne URL.
+ switch (t[0]) {
+ case 'P': /* POST? */
+ p = t.to_charp(1, 4);
+ if (!strcmp(p, "OST ")) {
+ p = t.to_charp(5);
+ post = true;
+ break;
+ }
+ case 'G': /* GET? */
+ p = t.to_charp(1, 3);
+ if (!strcmp(p, "ET ")) {
+ p = t.to_charp(4);
+ break;
+ }
+ default:
+ bad = true;
+ }
+
+ if (!bad) {
+ ssize_t poshttp, posslash;
+ Uri = p;
+ sppos = Uri.strrchr(' ');
+ p = Uri.to_charp(0, sppos - 1);
+ Uri = p;
+ // On enlève tout le host spécifié éventuellement dans la requete.
+ if ((poshttp = Uri.strstr("http://")) > 0) {
+ Uri = Uri.to_charp(poshttp + 7);
+ posslash = Uri.strchr('/');
+ // Certains navigateurs indiqueront uniquement http://host comme URL.
+ if (posslash >= 0) {
+ Uri = Uri.to_charp(posslash);
+ } else {
+ Uri = "";
+ }
+ }
+ posslash = Uri.strrchr('/');
+ file = Uri.to_charp(posslash + 1);
+ if (posslash > 0) {
+ domain = Uri.to_charp(0, posslash - 1);
+ } else {
+ domain = "";
+ }
+ }
+ return post;
+}
+
+/*
+ * Ceci sert à rediriger le navigateur vers l'url de démarrage.
+ */
+void HttpServ::SendRedirect(Socket & s) {
+ s << "HTTP/1.1 301 Moved Permanently" << endhl <<
+ "Server: " << name << endhl <<
+ "Location: http://127.0.0.1:" << localport << "/bin/start" << endhl <<
+ "Cache-Control: no-cache" << endhl <<
+ "Connection-Type: closed" << endhl <<
+ "Content-Type: text/html" << endhl << endhl <<
+ "<HTML><HEAD><TITLE>301 - Moved Permanently</TITLE></HEAD>" << endnl <<
+ "<BODY><center><b><h2>You should be redirected to the " << endnl << endnl <<
+ "<a href=\"http://localhost/bin/start\">start page</a></h2></b></center>" << endnl <<
+ "</BODY></HTML>" << endnl;
+}
+
+/*
+ * Nous envoyons les entetes de réponse HTTP.
+ */
+
+void HttpServ::SendHeads(Socket & s, const String & mime) {
+ s << "HTTP/1.1 200 OK" << endhl <<
+ "Server: " << name << endhl <<
+ "Cache-Control: no-cache" << endhl <<
+ "Connection-Type: closed" << endhl <<
+ "Content-Type: " << mime << endhl << endhl;
+}
+
+/*
+ * Affichage d'une erreur 404.
+ */
+
+void HttpServ::ShowError(Socket & s) {
+ s << "HTTP/1.1 404 Not Found" << endhl <<
+ "Server: " << name << endhl <<
+ "Cache-Control: no-cache" << endhl <<
+ "Connection-Type: closed" << endhl <<
+ "Content-Type: text/html" << endhl << endhl <<
+ "<HTML><HEAD><TITLE>404 - Error</TITLE></HEAD>" << endnl <<
+ "<BODY><center><b><h2>The server was unable to process your query</h2></b></center>" << endnl <<
+ "Click <A HREF=\"/\">here</A> to go the main page." <<
+ "</BODY></HTML>" << endnl;
+}
+
+/*
+ * Sert à déterminer le type mime à partir de l'extension du fichier.
+ * Par défaut, nous mettons "text/plain".
+ */
+
+String HttpServ::GetMime(const String & f) {
+ String ext;
+ size_t ppos;
+
+ ppos = f.strrchr('.');
+
+ if (ppos >= 0) {
+ ext = f.to_charp(ppos + 1);
+ if (ext == "jpg") return "image/jpeg";
+ if (ext == "jpeg") return "image/jpeg";
+ if (ext == "htm") return "text/html";
+ if (ext == "html") return "text/html";
+ if (ext == "gif") return "image/gif";
+ if (ext == "png") return "image/png";
+ }
+
+ return "text/plain";
+}
diff --git a/lib/Input.cc b/lib/Input.cc
new file mode 100644
index 0000000..f4e2602
--- /dev/null
+++ b/lib/Input.cc
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "Input.h"
+#include "Exceptions.h"
+#include "config.h"
+
+Input::Input(String no) throw (IOGeneral) :
+ Handle(no.strlen() ? open(no.to_charp(), O_RDONLY) : 0),
+ n(no) {
+ if (GetHandle() < 0) {
+ throw IOGeneral(String("Error opening file") + no + " for reading: " + strerror(errno));
+ }
+}
+
+bool Input::CanWrite() {
+ return 0;
+}
+
+bool Input::CanRead() {
+ return 1;
+}
+
+String Input::GetName() {
+ return n;
+}
+
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..5149387
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,12 @@
+localedir = $(datadir)/locale
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+AM_CFLAGS = -O3 -Wall -Wstrict-prototypes $(CFLAGS)
+AM_CXXFLAGS = -O3 -Wall -Wstrict-prototypes $(CXXFLAGS)
+INCLUDES = -I. -I.. -I$(includedir) -I../include
+lib_LTLIBRARIES = libClasses.la
+
+libClasses_la_SOURCES = Exceptions.cc Handle.cc Output.cc String.cc\
+ Socket.cc Input.cc HttpServ.cc Variables.cc Action.cc Menu.cc Message.cc\
+ Form.cc Confirm.cc Table.cc checkargs.c datecalc.c
+
+libClasses_la_LDFLAGS = -version-info $(Classes_VERSION_INFO)
diff --git a/lib/Menu.cc b/lib/Menu.cc
new file mode 100644
index 0000000..7488d1d
--- /dev/null
+++ b/lib/Menu.cc
@@ -0,0 +1,26 @@
+#include "Menu.h"
+#include "HttpServ.h"
+
+Menu::Menu(const String & t, const String & U, String * ts, Action ** as, int nb) :
+ Action(U), tit(t), lt(ts), la(as), nba(nb) { }
+
+void Menu::Do(Variables * v, Handle * h) {
+ int i, f = 0;
+
+ SendHead(h);
+ (*h) << "<center><TABLE BORDER=0>" << endnl;
+ for (i = 0; i < nba; i++) {
+ (*h) << "<TR BGCOLOR=\"#" << (f ? "dddddd" : "cccccc") << "\"><TD ALIGN=\"center\">" << (i + 1) << "</TD><TD>"
+ "<A HREF=\"/bin/" << (la[i] ? la[i]->GetURL() : "start") << "\">"
+ << lt[i] << "</A></TD></TR>" << endnl;
+ f = f ? 0 : 1;
+ }
+ (*h) << "</TABLE></center>" << endnl;
+ SendFoot(h);
+
+ Accessed();
+}
+
+String Menu::GetTitle(void) {
+ return tit;
+}
diff --git a/lib/Message.cc b/lib/Message.cc
new file mode 100644
index 0000000..1ede8fb
--- /dev/null
+++ b/lib/Message.cc
@@ -0,0 +1,21 @@
+#include "HttpServ.h"
+#include "Message.h"
+#include "config.h"
+
+Message::Message(const String & t, const String & m, const String & U, Action * n) :
+ Action(U), tit(t), msg(m), Next(n) { }
+
+void Message::Do(Variables * v, Handle * h) {
+ SendHead(h);
+ (*h) << msg << "<CENTER><FORM METHOD=\"POST\" ACTION=\"/bin/" << (Next ? Next->GetURL() : "start") << "\">" << endnl <<
+ "<INPUT TYPE=\"SUBMIT\" VALUE=\" Ok \">" << endnl;
+ v->Dump(h);
+ (*h) << "</FORM></CENTER>" << endnl;
+ SendFoot(h);
+
+ Accessed();
+}
+
+String Message::GetTitle(void) {
+ return tit;
+}
diff --git a/lib/Output.cc b/lib/Output.cc
new file mode 100644
index 0000000..75bb437
--- /dev/null
+++ b/lib/Output.cc
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "Output.h"
+#include "Exceptions.h"
+#include "config.h"
+
+Output::Output(String no, int trunc = 1) throw (IOGeneral) :
+ Handle(no.strlen() ? open(no.to_charp(), O_WRONLY | O_CREAT | (trunc ? O_TRUNC : O_APPEND)) : 1),
+ n(no) {
+ if (GetHandle() < 0) {
+ throw IOGeneral(String("Error opening file") + no + " for writing: " + strerror(errno));
+ }
+}
+
+bool Output::CanWrite() {
+ return 1;
+}
+
+bool Output::CanRead() {
+ return 0;
+}
+
+String Output::GetName() {
+ return n;
+}
+
diff --git a/lib/Socket.cc b/lib/Socket.cc
new file mode 100644
index 0000000..100d434
--- /dev/null
+++ b/lib/Socket.cc
@@ -0,0 +1,140 @@
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include "String.h"
+#include "Socket.h"
+#include "Exceptions.h"
+#include "config.h"
+#include "Input.h"
+#include "Output.h"
+
+Socket::Socket() throw (GeneralException) : Handle(socket(AF_INET, SOCK_STREAM, 0)), connected(false), listening(false) {
+ if (GetHandle() < 0) {
+ throw GeneralException("Error creating socket.");
+ }
+}
+
+Socket::Socket(int h) : Handle(h), connected(true), listening(false) { }
+
+String Socket::GetName(void) {
+ return String("socket");
+}
+
+bool Socket::IsConnected(void) {
+ return connected;
+}
+
+bool Socket::IsListening(void) {
+ return listening;
+}
+
+bool Socket::CanRead(void) {
+ return connected;
+}
+
+bool Socket::CanWrite(void) {
+ return connected;
+}
+
+size_t Socket::WriteFile(Output & o) {
+ char c;
+ size_t s = 0;
+
+ while (!IsClosed()) {
+ s += read(&c, 1);
+ o.write(&c, 1);
+ }
+ return s;
+}
+
+size_t Socket::ReadFile(Input & i) {
+ char c;
+ size_t s = 0;
+
+ while (!i.IsClosed()) {
+ s += i.read(&c, 1);
+ write(&c, 1);
+ }
+ return s;
+}
+
+
+ /***********************************************\
+ * Toute la suite n'est pas à décrire. Consulter *
+ * plutôt un document décrivant les sockets. *
+ \***********************************************/
+
+
+bool Socket::SetLocal(String vhost, int port) {
+ struct hostent * localhostent;
+ struct in_addr localhostaddr;
+ struct sockaddr_in localsocketaddr;
+
+
+ memset((void *)&localhostaddr, 0, sizeof(localhostaddr));
+
+ if (vhost.strlen() != 0) {
+ if ((localhostent = gethostbyname(vhost.to_charp()))) {
+ memcpy((void *)&localhostaddr, localhostent->h_addr, sizeof(localhostaddr));
+ } else {
+ return false;
+ }
+ } else {
+ localhostaddr.s_addr = htonl(INADDR_ANY);
+ }
+
+ memset(&localsocketaddr, 0, sizeof(struct sockaddr_in));
+ localsocketaddr.sin_family = AF_INET;
+ localsocketaddr.sin_addr = localhostaddr;
+ localsocketaddr.sin_port = htons(port);
+
+ if (bind(GetHandle(), (struct sockaddr *) &localsocketaddr, sizeof(localsocketaddr)) < 0) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool Socket::Connect(String host, int port) {
+ struct hostent * remotehostent;
+ struct sockaddr_in remotesocketaddr;
+
+ if (!listening && !connected) {
+ if (!(remotehostent = gethostbyname(host.to_charp()))) {
+ return false;
+ }
+
+ remotesocketaddr.sin_family = AF_INET;
+ remotesocketaddr.sin_port = htons(port);
+ bcopy(remotehostent->h_addr, &remotesocketaddr.sin_addr, remotehostent->h_length);
+ if(connect(GetHandle(), (struct sockaddr *)&remotesocketaddr, sizeof(remotesocketaddr)) < 0) {
+ connected = true;
+ }
+ }
+ return connected;
+}
+
+bool Socket::Listen(void) {
+ if (!listening && !connected) {
+ if (listen(GetHandle(), 10)) {
+ listening = true;
+ }
+ }
+ return listening;
+}
+
+Socket Socket::Accept(void) {
+ struct sockaddr inaddr;
+ socklen_t inlen = sizeof(inaddr);
+ int h;
+
+ if ((h = accept(GetHandle(), &inaddr, &inlen)) < 0) {
+ return Socket();
+ } else {
+ return Socket(h);
+ }
+}
diff --git a/lib/String.cc b/lib/String.cc
new file mode 100644
index 0000000..16c7db3
--- /dev/null
+++ b/lib/String.cc
@@ -0,0 +1,291 @@
+#include <iostream.h>
+#include <string.h>
+#include <stdarg.h>
+#include "String.h"
+#include "Exceptions.h"
+#include "config.h"
+
+extern "C" {
+ double dateCalc(char *, char *);
+ int isDateArgument(char *);
+}
+
+char String::t[BUFSIZ];
+
+String::String(const String & s) : str(strdup(s.str)) { }
+
+String::String(char c) {
+ char t[2];
+
+ sprintf(t, "%c", c);
+ str = strdup(t);
+}
+
+String::String(const char * s) : str(s ? strdup(s) : strdup("")) { }
+
+String::String(int i) {
+ char t[20];
+
+ sprintf(t, "%i", i);
+ str = strdup(t);
+}
+
+String::String(double d) {
+ char t[30];
+
+ sprintf(t, "%g", d);
+ str = strdup(t);
+}
+
+String::~String() {
+ free(str);
+}
+
+char * String::set(char * s, ...) {
+ va_list ap;
+
+ va_start(ap, s);
+ vsprintf(t, s, ap);
+ free(str);
+ str = strdup(t);
+ va_end(ap);
+ return t;
+}
+
+char * String::to_charp(size_t from, ssize_t to) const {
+ if (to < 0) {
+ strcpy(t, &(str[from]));
+ } else {
+ if (to >= strlen()) {
+ to = strlen() - 1;
+ }
+ if (to >= from) {
+ int i;
+ for (i = 0; i <= to - from; i++) {
+ t[i] = str[i + from];
+ }
+ t[i] = '\0';
+ } else {
+ t[0] = '\0';
+ }
+ }
+ return t;
+}
+
+int String::to_int(void) const {
+ int r;
+
+ sscanf(str, "%i", &r);
+ return r;
+}
+
+double String::to_double(void) const {
+ double r;
+
+ sscanf(str, "%lf", &r);
+ return r;
+}
+
+String & String::operator=(const String & s) {
+ if (str != s.str) {
+ // On évite l'autodestruction...
+ free(str);
+ str = strdup(s.str);
+ }
+ return *this;
+}
+
+String String::operator+(const String & s) const {
+ strcpy(t, str);
+ strcat(t, s.str);
+ return String(t);
+}
+
+String & String::operator+=(const String & s) {
+ strcpy(t, str);
+ strcat(t, s.str);
+ free(str);
+ str = strdup(t);
+ return (*this);
+}
+
+ostream & operator<<(ostream & os, const String & s) {
+ return (os << s.to_charp());
+}
+
+istream & operator>>(istream & is, String & s) {
+ char c;
+
+ s.set("");
+
+ while (!is.eof()) {
+ is >> c;
+ s += c;
+ }
+
+ return is;
+}
+
+bool String::operator!=(const String & s) const {
+ return (strcmp(str, s.str) != 0);
+}
+
+bool String::operator==(const String & s) const {
+ return (strcmp(str, s.str) == 0);
+}
+
+bool String::operator<=(const String & s) const {
+ return (strcmp(str, s.str) <= 0);
+}
+
+bool String::operator>=(const String & s) const {
+ return (strcmp(str, s.str) >= 0);
+}
+
+bool String::operator<(const String & s) const {
+ return (strcmp(str, s.str) < 0);
+}
+
+bool String::operator>(const String & s) const {
+ return (strcmp(str, s.str) > 0);
+}
+
+size_t String::strlen() const {
+ return (str ? ::strlen(str) : 0);
+}
+
+char String::operator[](size_t i) const {
+ if (i >= strlen()) {
+ return 0;
+ } else {
+ return str[i];
+ }
+}
+
+ssize_t String::strchr(char c, size_t from) const {
+ size_t s = strlen();
+
+ for (size_t i = from; i < s; i++) {
+ if (str[i] == c) return i;
+ }
+
+ return -1;
+}
+
+ssize_t String::strrchr(char c) const {
+ size_t s = strlen();
+
+ for (size_t i = s - 1; i >= 0; i--) {
+ if (str[i] == c) return i;
+ }
+
+ return -1;
+}
+
+ssize_t String::strstr(const String & s) const {
+ char * p = ::strstr(str, s.str);
+
+ if (p) {
+ return p - str;
+ } else {
+ return -1;
+ }
+}
+
+int String::strchrcnt(char c) const {
+ size_t i, cnt = 0;
+ size_t s = strlen();
+
+ for (i = 0; i < s; i++) {
+ if (str[i] == c) cnt++;
+ }
+
+ return cnt;
+}
+
+String String::to_sqldate(void) const {
+/* DD/MM/YYYY ==> YYYYMMMDD */
+ return (is_date() ? String(to_charp(6, 9)) + to_charp(3, 4) + to_charp(0, 1) : "");
+}
+
+String String::to_sqltime(void) const {
+/* h:m ==> h * 60 + m */
+ int p = strchr(':');
+ return (is_time() ? String(String(to_charp(0, p - 1)).to_int() * 60 + String(to_charp(p + 1)).to_int()) : "");
+}
+
+String String::from_sqldate(void) const {
+/* YYYYMMDD ==> DD/MM/YYYY */
+ return ((strlen() == 8) && is_number() ? String(to_charp(6, 7)) + '/' + to_charp(4, 5) + '/' + to_charp(0, 3) : "");
+}
+
+String String::from_sqltime(void) const {
+/* t ==> (t / 60):(t % 60) */
+ int t = to_int();
+ return (is_number() ? String((int) (t / 60)) + ':' + (t % 60) : "");
+}
+
+bool String::is_date(void) const {
+/* 'DD/MM/YYYY'
+ 0123456789 */
+
+ if (strlen() != 10) return false;
+ if ((str[2] != '/') || (str[5] != '/') ||
+ (!String(to_charp(0, 1)).is_number()) ||
+ (!String(to_charp(3, 4)).is_number()) ||
+ (!String(to_charp(6, 9)).is_number())) {
+ return (isDateArgument(to_sqldate().str));
+ }
+
+ return true;
+}
+
+double String::datedif(const String & s) const {
+ double r;
+ if (is_date() && s.is_date()) {
+ r = dateCalc(str, s.str);
+ return r < 0 ? -r : r;
+ }
+
+ return -1;
+}
+
+bool String::is_number(void) const {
+ size_t s = strlen();
+
+
+ for (size_t i = ((str[i] == '-') ? 1 : 0); i < s; i++) {
+ if ((str[i] > '9') || (str[i] < '0')) return false;
+ }
+ return true;
+}
+
+bool String::is_float(void) const {
+ size_t s = strlen();
+ bool seendot = false;
+
+ for (size_t i = ((str[i] == '-') ? 1 : 0); i < s; i++) {
+ if ((str[i] > '9') || (str[i] < '0')) {
+ if ((str[i] == '.') && !seendot) {
+ seendot = true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool String::is_time(void) const {
+ int p = strchr(':');
+
+ if (p == -1) return false;
+
+ // On accepte les heures sous le format xxxxxx:yy pour pouvoir indiquer des durées.
+
+ if ((!String(to_charp(0, p - 1)).is_number()) || (!String(to_charp(p + 1)).is_number()))
+ return false;
+
+ return (String(to_charp(p + 1)).to_int() < 60) ? true : false;
+}
diff --git a/lib/Table.cc b/lib/Table.cc
new file mode 100644
index 0000000..776caae
--- /dev/null
+++ b/lib/Table.cc
@@ -0,0 +1,41 @@
+#include "Table.h"
+#include "HttpServ.h"
+
+Table::Table(const String & titre, const String & url, String * heads, String * cells, int nbc, int nbl, Action * na) :
+ Action(url), tit(titre), hds(heads), cls(cells), nc(nbc), nl(nbl), Next(na) { }
+
+String Table::GetTitle(void) {
+ return tit;
+}
+
+void Table::Do(Variables * v, Handle * h) {
+ SendHead(h);
+
+ (*h) << "<center><TABLE BORDER=0>" << endnl;
+
+ if (hds) {
+ (*h) << "<TR>" << endnl;
+ for (int i = 0; i < nc; i++) {
+ (*h) << "<TH BGCOLOR=\"#bbbbbb\">" << hds[i] << "</TH>" << endnl;
+ }
+ (*h) << "</TR>" << endnl;
+ }
+
+ for (int l = 0; l < nl; l++) {
+ (*h) << "<TR>" << endnl;
+ for (int c = 0; c < nc; c++) {
+ (*h) << "<TD BGCOLOR=\"#" << (l % 2 ? "cccccc" : "dddddd") << "\">" << cls[l * nc + c] << "</TD>" << endnl;
+ }
+ (*h) << "</TR>" << endnl;
+ }
+
+ (*h) << "</TABLE>"
+
+"<FORM METHOD=\"POST\" ACTION=\"/bin/" << (Next ? Next->GetURL() : "start") << "\">" << endnl <<
+"<INPUT TYPE=\"SUBMIT\" VALUE=\" Ok \">" << endnl;
+(*h) << "</FORM></CENTER>" << endnl;
+
+
+ SendFoot(h);
+ Accessed();
+}
diff --git a/lib/Variables.cc b/lib/Variables.cc
new file mode 100644
index 0000000..15de09d
--- /dev/null
+++ b/lib/Variables.cc
@@ -0,0 +1,55 @@
+
+#include "Variables.h"
+#include "HttpServ.h"
+#include "String.h"
+#include "config.h"
+
+Variables::Variables(int nb) : Vars(nb ? new String[nb] : 0), nbvars(nb) { }
+
+Variables::~Variables() {
+ if (Vars) {
+ delete[] Vars;
+ }
+}
+
+void Variables::SetTo(int i, const String & s) {
+ Vars[i] = s;
+}
+
+String Variables::operator[](const String & name) {
+ int i;
+ String r;
+
+ for (i = 0; i < nbvars; i++) {
+ if (Vars[i].strstr(name) == 0) break;
+ }
+
+ if (i == nbvars) {
+ r = "";
+ } else {
+ r = Vars[i].to_charp(Vars[i].strchr('=') + 1);
+ }
+
+ return r;
+}
+
+String Variables::operator[](int i) {
+ return Vars[i];
+}
+
+int Variables::GetNb(void) {
+ return nbvars;
+}
+
+void Variables::Dump(Handle * h) {
+ int i, eqp;
+ String Vn, Vv;
+
+ for (i = 0; i < nbvars; i++) {
+ eqp = Vars[i].strchr('=');
+ Vn = Vars[i].to_charp(0, eqp - 1);
+ Vv = Vars[i].to_charp(eqp + 1);
+ (*h) << "<INPUT TYPE=\"HIDDEN\" NAME=\"" << Vn << "\" VALUE=\"" << Vv << "\">" << endnl;
+ }
+}
+
diff --git a/lib/checkargs.c b/lib/checkargs.c
new file mode 100644
index 0000000..e1c97be
--- /dev/null
+++ b/lib/checkargs.c
@@ -0,0 +1,85 @@
+/* datedif - calculates the difference in days between two dates
+ * Copyright (C) 2000 Micael Widell contact: xeniac@linux.nu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+int isDateArgument(char* dateString) {
+
+ const int MONTHS[] = {31,0,31,30,31,30,31,31,30,31,30,31};
+ int i, day, month, year;
+ char buffer[5];
+
+ /* 'today' is a valid date */
+ if (strcmp(dateString, "today") == 0)
+ return 1;
+
+ /* Numeric dates must be eight characters long */
+ if (8 != strlen(dateString))
+ return 0;
+
+ /* Check that the date is entirely formed of numbers */
+ for (i = 0; i < 8; i++) {
+ if (dateString[i] < '0' || dateString[i] > '9')
+ return 0;
+ }
+
+ /* Check that the date exists */
+ memset(buffer, 0, 5);
+ strncpy(buffer, dateString + 6, 2);
+ day = atoi(buffer);
+ strncpy(buffer, dateString + 4, 2);
+ month = atoi(buffer);
+ month -= 1;
+ strncpy(buffer, dateString, 4);
+ year = atoi(buffer);
+
+ /* Validate month */
+ if (month < 0 || month > 11)
+ return 0;
+
+ /* Validating dates is simple when the date does not fall into February */
+ if (1 != month) {
+ if (day < 1 || day > MONTHS[month])
+ return 0;
+ } else {
+ int feb = 28;
+
+ /* Februarys are a bit tougher issue */
+ if (0 == year % 4) {
+ if (0 == year % 100) {
+ if (0 == year % 400) {
+ feb = 29;
+ } else {
+ feb = 28;
+ }
+ } else {
+ feb = 29;
+ }
+ }
+ if (day < 1 || day > feb)
+ return 0;
+ }
+
+ /* Avoid user from using dates before 16000301, since those will result in
+ incorrect output */
+ if(16000301 > atoi(dateString))
+ return 0;
+
+ return 1;
+}
diff --git a/lib/datecalc.c b/lib/datecalc.c
new file mode 100644
index 0000000..46e7179
--- /dev/null
+++ b/lib/datecalc.c
@@ -0,0 +1,84 @@
+/* datedif - calculates the difference in days between two dates
+ * Copyright (C) 2000 Micael Widell contact: xeniac@linux.nu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Gaus's formula - days since 1.3.1600 (Gregorian calendar) */
+int days(register int n, register int m, register int y)
+{
+ register int cy;
+ if((m -= 2) <= 0){
+ m += 12; y--;
+ }
+ y -= 1600; cy = y/100;
+ return 365*y+y/4-cy+cy/4+367*m/12+n-31;
+}
+
+
+double dateCalc(char date1[], char date2[]){
+
+ /* Declare the needed variables */
+ char* date[2] = { date1, date2 };
+ struct tm *date_tm[2];
+ time_t date_time_t[2];
+ double dateDifference;
+ int isToday[2] = { 0, 0 };
+ char buffer[5];
+ int day[2], month[2], year[2], i;
+
+
+ /* If any of the arguments are "today", then include today's date in the
+ right variables */
+ for(i = 0; i < 2; i++){
+ if(!strcmp(date[i], "today")){
+ time(&date_time_t[i]);
+ date_tm[i] = localtime(&date_time_t[i]);
+ day[i] = (*date_tm[i]).tm_mday;
+ month[i] = (*date_tm[i]).tm_mon + 1;
+ year[i] = (*date_tm[i]).tm_year + 1900;
+ isToday[i] = 1;
+ }
+ }
+
+ /* Cut out the year, month and day from 8-digit datestrings */
+ for (i = 0; i < 2; i++){
+ if(!isToday[i]){
+ memset(buffer, 0, 5);
+ strncpy(buffer, &date[i][6], 2);
+ day[i] = atoi(buffer);
+ strncpy(buffer, &date[i][4], 2);
+ month[i] = atoi(buffer);
+ strncpy(buffer, date[i], 4);
+ year[i] = atoi(buffer);
+ }
+ }
+
+ /* Calculate the difference */
+ dateDifference = days(day[1], month[1], year[1]) - days(day[0], month[0], year[0]);
+
+ return dateDifference;
+}
+
+
+
+
+
+