News Uni Dev Res Blag

ps3serv

2011/03/27 - 03:18

Want to transfer large files efficiently from one computer to the other? The most obvious solution is an ethernet cable. Surprisingly difficult, as most webservers (including lighttpd and apache) seems to expect a router in-between, otherwise all the files become corrupted mid-transfer.

I've usually circumvented this issue by using tar | nc, but this faltered when I wanted to transfer videos to my PS3. Now, I could read through endless documentation and bugtrackers for hours, or I could fix it the fun way. Cue the C# webserver.

Note: This was written for a unix environment, and thus it uses the unix folder separator. This may cause problems in the Windurr world (I haven't tested yet). (a possible fix is a global char called `s' or whatever, enjoy your mess)

Written on a night train, 2011/02/09

Screenshot

ps3serv.cs

/* ps3serv.cs - http fileserver that works over xlan
 * Copyright (C) 2011  ed <irc.rizon.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, please refer to the following URL:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 */

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace ps3serv
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            new MainClass().run(args);
        }
        
        private void debug (int flag, string str)
        {
            //flag = 0; //for non-VT100 terminals
            switch (flag)
            {
                case 0: break;
                case 1: Console.Write( // red
                                      (char)27 + "[0;31m <X> " +
                                      (char)27 + "[1;31m"); break;
                case 2: Console.Write( // yellow
                                      (char)27 + "[0;33m -!- " +
                                      (char)27 + "[1;33m"); break;
                case 3: Console.Write( // green
                                      (char)27 + "[0;32m OK> " +
                                      (char)27 + "[1;32m"); break;
                case 4: Console.Write( // blue?
                                      (char)27 + "[0;36m in: " +
                                      (char)27 + "[1;36m"); break;
            }
            if (flag > 0)
            {
                Console.WriteLine(str + (char)27 + "[0m");
            }
            else
            {
                Console.WriteLine(str);
            }
        }
        
        private string getSize(double size)
        {
            int isi = 0;
            string[] si = { "B.", "kB", "MB", "GB", "TB" };
            while (size > 1024)
            {
                size /= 1024;
                isi++;
            }
            if (size >= 100) size = Math.Round(size, 1);
            if (size >= 10) size = Math.Round(size, 2);
            if (size >= 1) size = Math.Round(size, 3);
            return size + " " + si[isi];
        }
        
        public void run (string[] args)
        {
            string root = "pub/";
            Directory.CreateDirectory(root);
            string rootF = System.IO.Path.GetFullPath("pub");
            debug(3, rootF);
            
            int port = 8086;
            if (args.Length > 0)
            {
                port = Convert.ToInt32(args[0]);
            }
            
            TcpListener server = new TcpListener(IPAddress.Any, port);
            server.Start();
            while (true)
            {
                debug(4, "~" + port);
                TcpClient client = server.AcceptTcpClient();
                debug(4, client.Client.RemoteEndPoint.ToString());
                Stream stream = client.GetStream();
                StreamReader reader = new StreamReader(stream);
                StreamWriter writer = new StreamWriter(stream);
                string query = reader.ReadLine();
                if (!query.StartsWith("GET /") ||
                    !query.EndsWith(" HTTP/1.1"))
                {
                    debug(1, query);
                    writer.WriteLine("HTTP/1.1 400 Bad Request");
                    writer.WriteLine();
                    writer.WriteLine("no.");
                    writer.Flush();
                    client.Close();
                    continue;
                }
                while (reader.ReadLine().Length > 0);
                query = query.Split(' ')[1].Substring(1);
                if (query != query.TrimStart('.', '/', '~'))
                {
                    debug(1, query);
                    writer.WriteLine("HTTP/1.1 400 Bad Request");
                    writer.WriteLine();
                    writer.WriteLine("&gt;he tries to sploit me");
                    writer.Flush();
                    client.Close();
                    continue;
                }
                query = query.Replace("+", "%2B"); //mono bug?
                query = System.Web.HttpUtility.UrlDecode(query);
                debug(2, "/" + query);
                string path = root + query;
                if (Directory.Exists(path))
                {
                    debug(2, "(folder listing)");
                    writer.WriteLine("HTTP/1.1 200 OK");
                    writer.WriteLine("Connection: close");
                    writer.WriteLine("Server: ps3serv/1.0");
                    writer.WriteLine("Content-Type: text/html; charset=utf-8");
                    writer.WriteLine();
                    writer.WriteLine("<html><head>" +
                                     "<title>ps3serv file listing</title>" +
                                     "<link rel=\"stylesheet\" type=\"text/css\" href=\"/main.css\" />");
                    //writer.WriteLine("<style>" + dircss + "</style>");
                    
                    writer.Write("<h1><a href=\"/\">~</a>");
                    string[] pathway = query.Split('/');
                    string pathlog = "";
                    foreach (string node in pathway)
                    {
                        pathlog += "/" + node;
                        writer.Write("\n\t / <a href=\"" + pathlog + "/\">" + node + "</a>");
                    }
                    writer.WriteLine(string.Format("<h2>{0}, {1}</h2>",
                                                   DateTime.Now.ToLongDateString(),
                                                   DateTime.Now.ToLongTimeString()));
                    
                    writer.WriteLine("<div id=\"list\">" +
                                     "<table><tr>" +
                                     "<td>Name</td>" +
                                     "<td>Size</td>" +
                                     "<td>Access</td>" +
                                     "<td>Modified</td>" +
                                     "</tr>");
                    
                    foreach (string dir in Directory.GetDirectories(path))
                    {
                        debug(2, "    " + dir);
                        FileInfo fi = new FileInfo(dir);
                        string name = Path.GetFileName(dir);
                        string href = dir.Substring(root.Length);
                        writer.WriteLine(string.Format(
                            "<tr>" +
                                "<td class=\"link\"><a href=\"{0}/\">{1}</a></td>" +
                                "<td class=\"size\">{2}</td>" +
                                "<td class=\"attr\">{3}</td>" + 
                                "<td class=\"time\">{4}</td>" +
                            "</tr>", //lole
                            name, name, "-",
                            fi.Attributes.ToString(),
                            fi.LastWriteTime.ToString("yyyy-mm-dd HH:mm:ss")));
                    }
                    
                    foreach (string file in Directory.GetFiles(path))
                    {
                        debug(2, "    " + file);
                        FileInfo fi = new FileInfo(file);
                        string name = Path.GetFileName(file);
                        string href = file.Substring(root.Length);
                        writer.WriteLine(string.Format(
                            "<tr>" +
                                "<td class=\"link\"><a href=\"{0}\">{1}</a></td>" +
                                "<td class=\"size\">{2}</td>" +
                                "<td class=\"attr\">{3}</td>" + 
                                "<td class=\"time\">{4}</td>" +
                            "</tr>", //lole
                            name, name,
                            getSize(fi.Length),
                            fi.Attributes.ToString(),
                            fi.LastWriteTime.ToString("yyyy-mm-dd HH:mm:ss")));
                    }
                    writer.WriteLine("</table></div>");
                    writer.WriteLine("<h3>directory listing - ps3serv v1.0</h3>");
                }
                else if (File.Exists(path))
                {
                    debug(2, "(file transmission)");
                    writer.WriteLine("HTTP/1.1 200 OK");
                    writer.WriteLine("Connection: close");
                    writer.WriteLine("Server: ps3serv/1.0");
                    //writer.WriteLine("Content-Disposition: attachment");
                    writer.WriteLine("Content-Length: " + new FileInfo(path).Length);
                    writer.WriteLine();
                    writer.Flush();
                    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                    {
                        byte[] buffer = new byte[8192];
                        while (true)
                        {
                            int n = fs.Read(buffer, 0, buffer.Length);
                            if (n <= 0) break;
                            stream.Write(buffer, 0, n);
                        }
                    }
                }
                else
                {
                    writer.WriteLine("HTTP/1.1 404 Not Found");
                    writer.WriteLine("Connection: close");
                    writer.WriteLine("Server: ps3serv/1.0");
                    writer.WriteLine("Content-Type: text/html; charset=utf-8");
                    writer.WriteLine();
                    writer.WriteLine("nothing here brah");
                }
                writer.Flush();
                client.Close();
            }
        }
    }
}

main.css

html, body {
    color: #000;
    background: #eee;
    font-family: sans-serif;
    margin: 0;
    padding: 0;
    width: 100%;
}
h1 {
    margin: 4px 0;
    padding: 0 16px;
    font-size: 2em;
}
h2 {
    margin: 4px 0;
    padding: 0 16px;
    font-size: 1em;
}
h3 {
    text-align: right;
    font-weight: normal;
    font-style: italic;
    margin-right: 1em;
    font-size: .8em;
}
h1 a {
    color: #079;
}
#list {
    background: #fff;
    border: 1px solid #ccc;
    border-width: 1px 0;
    width: 98%;
    padding: 1%;
}
td {
    font-family: monospace;
    padding: 4px 16px;
    border: 1px solid #ccc;
    border-width: 0 0 1px 0;
}
td a {
    color: #069;
    text-decoration: none;
    margin-left: -1em;
}
td a:visited {
    color: #90b;
}
td a:hover {
    color: #29b;
}
td a:before {
    content: '>';
    color: #acd;
}
td a:visited:before {
    color: #fff;
}
.size {
    text-align: right;
}

Copyright © 2010 Ed Edland