├── 9781430258544.jpg
├── LICENSE.txt
├── README.md
├── contributing.md
└── source
├── README.md
├── chapter01
├── README.md
├── getname.py
├── search1.py
├── search2.py
├── search3.py
├── search4.py
├── search4.txt
└── stringcodes.py
├── chapter02
├── README.md
├── big_sender.py
├── udp_broadcast.py
├── udp_local.py
└── udp_remote.py
├── chapter03
├── README.md
├── tcp_deadlock.py
└── tcp_sixteen.py
├── chapter04
├── README.md
├── dns_basic.py
├── dns_mx.py
└── www_ping.py
├── chapter05
├── README.md
├── blocks.py
├── examples.rst
└── streamer.py
├── chapter06
├── README.md
├── ca.crt
├── features.py
├── localhost.pem
├── safe_tls.py
└── test_tls.py
├── chapter07
├── README.md
├── client.py
├── in_zen1.py
├── in_zen2.py
├── inetd.conf
├── srv_async.py
├── srv_asyncio1.py
├── srv_asyncio2.py
├── srv_legacy1.py
├── srv_legacy2.py
├── srv_single.py
├── srv_threaded.py
├── test.sh
├── trace.out
└── zen_utils.py
├── chapter08
├── README.md
├── hashing.py
├── queuecrazy.py
├── queuepi.py
└── squares.py
├── chapter09
├── README.md
├── config.py
└── examples.doctest
├── chapter10
├── README.md
├── _test.py
├── timeapp_raw.py
├── timeapp_webob.py
├── timeapp_werkz.py
└── wsgi_env.py
├── chapter11
├── README.md
├── app_improved.py
├── app_insecure.py
├── attack.js
├── bank.py
├── csrf.html
├── csrf_auto.html
├── djbank
│ ├── __init__.py
│ ├── admin.py
│ ├── fixtures
│ │ └── start.json
│ ├── models.py
│ ├── settings.py
│ ├── templates
│ │ ├── base.html
│ │ ├── index.html
│ │ ├── pay.html
│ │ └── registration
│ │ │ └── login.html
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
├── manage.py
├── mscrape.py
├── rscrape1.py
├── rscrape2.py
├── static
│ └── style.css
├── templates
│ ├── base.html
│ ├── index.html
│ ├── login.html
│ ├── pay.html
│ └── pay2.html
└── tinysite
│ ├── further.html
│ ├── index.html
│ ├── page1.html
│ ├── page2.html
│ ├── page3.html
│ ├── page4.html
│ ├── page5.html
│ ├── page6.html
│ └── search.html
├── chapter12
├── README.md
├── attachment.gz
├── attachment.txt
├── build_basic_email.py
├── build_mime_email.py
├── build_unicode_email.py
├── display_email.py
├── display_structure.py
└── pre-python-3.4
│ ├── README.rst
│ ├── mime_decode.py
│ ├── mime_gen_alt.py
│ ├── mime_gen_basic.py
│ ├── mime_gen_both.py
│ ├── mime_headers.py
│ ├── mime_parse_headers.py
│ ├── mime_structure.py
│ ├── trad_gen_newhdrs.py
│ ├── trad_gen_simple.py
│ └── trad_parse.py
├── chapter13
├── README.md
├── debug.py
├── ehlo.py
├── login.py
├── simple.py
└── tls.py
├── chapter14
├── README.md
├── apopconn.py
├── download-and-delete.py
├── mailbox.py
└── popconn.py
├── chapter15
├── README.md
├── folder_info.py
├── folder_summary.py
├── open_imap.py
├── open_imaplib.py
└── simple_client.py
├── chapter16
├── README.md
├── sftp_get.py
├── shell.py
├── ssh_commands.py
├── ssh_simple.py
├── ssh_threads.py
├── telnet_codes.py
└── telnet_login.py
├── chapter17
├── README.md
├── advbinarydl.py
├── advbinaryul.py
├── asciidl.py
├── binarydl.py
├── binaryul.py
├── connect.py
├── dir.py
├── nlst.py
└── recursedl.py
├── chapter18
├── README.md
├── jsonrpc_client.py
├── jsonrpc_server.py
├── rpyc_client.py
├── rpyc_server.py
├── testfile.txt
├── xmlrpc_client.py
├── xmlrpc_introspect.py
├── xmlrpc_multicall.py
└── xmlrpc_server.py
├── requirements.txt
├── session.txt
└── tools
├── monkeys
└── _bootlocale.py
├── run.sh
└── two.sh
/9781430258544.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/foundations-of-python-network-programming-14/742320235e7a62854b7cbb4b21a4b1b3c6e07c08/9781430258544.jpg
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apress/foundations-of-python-network-programming-14/742320235e7a62854b7cbb4b21a4b1b3c6e07c08/LICENSE.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Apress Source Code
2 |
3 | This repository accompanies [*Foundations of Python Network Programming*](http://www.apress.com/9781430258544) by Brandon Rhodes and John Goerzen (Apress, 2014).
4 |
5 | 
6 |
7 | Download the files as a zip using the green button, or clone the repository to your machine using Git.
8 |
9 | ## Releases
10 |
11 | Release v1.0 corresponds to the code in the published book, without corrections or updates.
12 |
13 | ## Contributions
14 |
15 | See the file Contributing.md for more information on how you can contribute to this repository.
16 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to Apress Source Code
2 |
3 | Copyright for Apress source code belongs to the author(s). However, under fair use you are encouraged to fork and contribute minor corrections and updates for the benefit of the author(s) and other readers.
4 |
5 | ## How to Contribute
6 |
7 | 1. Make sure you have a GitHub account.
8 | 2. Fork the repository for the relevant book.
9 | 3. Create a new branch on which to make your change, e.g.
10 | `git checkout -b my_code_contribution`
11 | 4. Commit your change. Include a commit message describing the correction. Please note that if your commit message is not clear, the correction will not be accepted.
12 | 5. Submit a pull request.
13 |
14 | Thank you for your contribution!
--------------------------------------------------------------------------------
/source/README.md:
--------------------------------------------------------------------------------
1 |
2 | Underneath this `py3` directory is a folder for each chapter of the
3 | recent Third Edition of *Foundations of Python Network Programming* from
4 | Apress. To learn more, move one level up in this directory tree to read
5 | the [Table of Contents](https://github.com/brandon-rhodes/fopnp).
6 |
--------------------------------------------------------------------------------
/source/chapter01/README.md:
--------------------------------------------------------------------------------
1 | [Return to the Table of Contents](https://github.com/brandon-rhodes/fopnp#readme)
2 |
3 | # Chapter 1
Introduction to Client-Server Networking
4 |
5 | This is a directory of program listings from Chapter 1 of the book:
6 |
7 |
All you have to do is press the button.
7 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /source/chapter11/csrf_auto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |You are already a winner!
7 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /source/chapter11/djbank/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/foundations-of-python-network-programming-14/742320235e7a62854b7cbb4b21a4b1b3c6e07c08/source/chapter11/djbank/__init__.py -------------------------------------------------------------------------------- /source/chapter11/djbank/admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/djbank/admin.py 4 | # Admin site setup for our Django application. 5 | 6 | from django.contrib import admin 7 | from .models import Payment 8 | 9 | admin.site.register(Payment) 10 | -------------------------------------------------------------------------------- /source/chapter11/djbank/fixtures/start.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "djbank.payment", "pk": 1, "fields": { 4 | "debit": "brandon", "credit": "psf", "dollars": 125, 5 | "memo": "Registration for PyCon" 6 | } 7 | }, 8 | { 9 | "model": "djbank.payment", "pk": 2, "fields": { 10 | "debit": "brandon", "credit": "liz", "dollars": 200, 11 | "memo": "Payment for writing that code" 12 | } 13 | }, 14 | { 15 | "model": "djbank.payment", "pk": 3, "fields": { 16 | "debit": "sam", "credit": "brandon", "dollars": 25, 17 | "memo": "Gas money-thanks for the ride!" 18 | } 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /source/chapter11/djbank/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/djbank/models.py 4 | # Model definitions for our Django application. 5 | 6 | from django.db import models 7 | from django.forms import ModelForm 8 | 9 | class Payment(models.Model): 10 | debit = models.CharField(max_length=200) 11 | credit = models.CharField(max_length=200, verbose_name='To account') 12 | dollars = models.PositiveIntegerField() 13 | memo = models.CharField(max_length=200) 14 | 15 | class PaymentForm(ModelForm): 16 | class Meta: 17 | model = Payment 18 | fields = ['credit', 'dollars', 'memo'] 19 | -------------------------------------------------------------------------------- /source/chapter11/djbank/settings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/djbank/settings.py 4 | # Settings file for our Django application (see addition at bottom). 5 | # ---------------------------------------------------------------------- 6 | """ 7 | Django settings for djbank project. 8 | 9 | For more information on this file, see 10 | https://docs.djangoproject.com/en/1.7/topics/settings/ 11 | 12 | For the full list of settings and their values, see 13 | https://docs.djangoproject.com/en/1.7/ref/settings/ 14 | """ 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | import os 18 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 19 | 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | SECRET_KEY = 'iwz)*5w!=$zx%^fm!7tx=mr5zr_ggoa$^pan@2!7-!2!jkcfew' 26 | 27 | # SECURITY WARNING: don't run with debug turned on in production! 28 | DEBUG = True 29 | 30 | TEMPLATE_DEBUG = True 31 | 32 | ALLOWED_HOSTS = [] 33 | 34 | 35 | # Application definition 36 | 37 | INSTALLED_APPS = ( 38 | 'django.contrib.admin', 39 | 'django.contrib.auth', 40 | 'django.contrib.contenttypes', 41 | 'django.contrib.sessions', 42 | 'django.contrib.messages', 43 | 'django.contrib.staticfiles', 44 | 'djbank', 45 | ) 46 | 47 | MIDDLEWARE_CLASSES = ( 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 53 | 'django.contrib.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | ) 56 | 57 | ROOT_URLCONF = 'djbank.urls' 58 | 59 | WSGI_APPLICATION = 'djbank.wsgi.application' 60 | 61 | 62 | # Database 63 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 64 | 65 | DATABASES = { 66 | 'default': { 67 | 'ENGINE': 'django.db.backends.sqlite3', 68 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 69 | } 70 | } 71 | 72 | # Internationalization 73 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 74 | 75 | LANGUAGE_CODE = 'en-us' 76 | 77 | TIME_ZONE = 'UTC' 78 | 79 | USE_I18N = True 80 | 81 | USE_L10N = True 82 | 83 | USE_TZ = True 84 | 85 | 86 | # Static files (CSS, JavaScript, Images) 87 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 88 | 89 | STATIC_URL = '/static/' 90 | 91 | # ---------------------------------------------------------------------- 92 | # All of the above are the pristine settings as written by the 93 | # "startapp" command. Here are the additions necessary for the app as 94 | # published in Foundations of Python Network Programming: 95 | 96 | STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),) 97 | -------------------------------------------------------------------------------- /source/chapter11/djbank/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |Your Payments
9 |Your username and password didn't match. Please try again.
9 | {% endif %} 10 | 11 | 27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /source/chapter11/djbank/urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/djbank/urls.py 4 | # URL patterns for our Django application. 5 | 6 | from django.conf.urls import patterns, include, url 7 | from django.contrib import admin 8 | from django.contrib.auth.views import login 9 | 10 | urlpatterns = patterns('', 11 | url(r'^admin/', include(admin.site.urls)), 12 | url(r'^accounts/login/$', login), 13 | url(r'^$', 'djbank.views.index_view', name='index'), 14 | url(r'^pay/$', 'djbank.views.pay_view', name='pay'), 15 | url(r'^logout/$', 'djbank.views.logout_view'), 16 | ) 17 | -------------------------------------------------------------------------------- /source/chapter11/djbank/views.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/djbank/views.py 4 | # A function for each view in our Django application. 5 | 6 | from django.contrib import messages 7 | from django.contrib.auth.decorators import login_required 8 | from django.contrib.auth import logout 9 | from django.db.models import Q 10 | from django.shortcuts import redirect, render 11 | from django.views.decorators.http import require_http_methods, require_safe 12 | from .models import Payment, PaymentForm 13 | 14 | def make_payment_views(payments, username): 15 | for p in payments: 16 | yield {'dollars': p.dollars, 'memo': p.memo, 17 | 'prep': 'to' if (p.debit == username) else 'from', 18 | 'account': p.credit if (p.debit == username) else p.debit} 19 | 20 | @require_safe 21 | @login_required 22 | def index_view(request): 23 | username = request.user.username 24 | payments = Payment.objects.filter(Q(credit=username) | Q(debit=username)) 25 | payment_views = make_payment_views(payments, username) 26 | return render(request, 'index.html', {'payments': payment_views}) 27 | 28 | @require_http_methods(['GET', 'POST']) 29 | @login_required 30 | def pay_view(request): 31 | form = PaymentForm(request.POST or None) 32 | if form.is_valid(): 33 | payment = form.save(commit=False) 34 | payment.debit = request.user.username 35 | payment.save() 36 | messages.add_message(request, messages.INFO, 'Payment successful.') 37 | return redirect('/') 38 | return render(request, 'pay.html', {'form': form}) 39 | 40 | @require_safe 41 | def logout_view(request): 42 | logout(request) 43 | return redirect('/') 44 | -------------------------------------------------------------------------------- /source/chapter11/djbank/wsgi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/djbank/wsgi.py 4 | # Standard, unchanged WSGI callable produced by Django. 5 | # ---------------------------------------------------------------------- 6 | """ 7 | WSGI config for djbank project. 8 | 9 | It exposes the WSGI callable as a module-level variable named ``application``. 10 | 11 | For more information on this file, see 12 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 13 | """ 14 | 15 | import os 16 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djbank.settings") 17 | 18 | from django.core.wsgi import get_wsgi_application 19 | application = get_wsgi_application() 20 | -------------------------------------------------------------------------------- /source/chapter11/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/manage.py 4 | # The "manage.py" file produced by Django for the "djbank" application: 5 | # ---------------------------------------------------------------------- 6 | #!/usr/bin/env python 7 | import os 8 | import sys 9 | 10 | if __name__ == "__main__": 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djbank.settings") 12 | 13 | from django.core.management import execute_from_command_line 14 | 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /source/chapter11/mscrape.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/mscrape.py 4 | # Manual scraping, that navigates to a particular page and grabs data. 5 | 6 | import argparse, bs4, lxml.html, requests 7 | from selenium import webdriver 8 | from urllib.parse import urljoin 9 | 10 | ROW = '{:>12} {}' 11 | 12 | def download_page_with_requests(base): 13 | session = requests.Session() 14 | response = session.post(urljoin(base, '/login'), 15 | {'username': 'brandon', 'password': 'atigdng'}) 16 | assert response.url == urljoin(base, '/') 17 | return response.text 18 | 19 | def download_page_with_selenium(base): 20 | browser = webdriver.Firefox() 21 | browser.get(base) 22 | assert browser.current_url == urljoin(base, '/login') 23 | css = browser.find_element_by_css_selector 24 | css('input[name="username"]').send_keys('brandon') 25 | css('input[name="password"]').send_keys('atigdng') 26 | css('input[name="password"]').submit() 27 | assert browser.current_url == urljoin(base, '/') 28 | return browser.page_source 29 | 30 | def scrape_with_soup(text): 31 | soup = bs4.BeautifulSoup(text) 32 | total = 0 33 | for li in soup.find_all('li', 'to'): 34 | dollars = int(li.get_text().split()[0].lstrip('$')) 35 | memo = li.find('i').get_text() 36 | total += dollars 37 | print(ROW.format(dollars, memo)) 38 | print(ROW.format('-' * 8, '-' * 30)) 39 | print(ROW.format(total, 'Total payments made')) 40 | 41 | def scrape_with_lxml(text): 42 | root = lxml.html.document_fromstring(text) 43 | total = 0 44 | for li in root.cssselect('li.to'): 45 | dollars = int(li.text_content().split()[0].lstrip('$')) 46 | memo = li.cssselect('i')[0].text_content() 47 | total += dollars 48 | print(ROW.format(dollars, memo)) 49 | print(ROW.format('-' * 8, '-' * 30)) 50 | print(ROW.format(total, 'Total payments made')) 51 | 52 | def main(): 53 | parser = argparse.ArgumentParser(description='Scrape our payments site.') 54 | parser.add_argument('url', help='the URL at which to begin') 55 | parser.add_argument('-l', action='store_true', help='scrape using lxml') 56 | parser.add_argument('-s', action='store_true', help='get with selenium') 57 | args = parser.parse_args() 58 | if args.s: 59 | text = download_page_with_selenium(args.url) 60 | else: 61 | text = download_page_with_requests(args.url) 62 | if args.l: 63 | scrape_with_lxml(text) 64 | else: 65 | scrape_with_soup(text) 66 | 67 | if __name__ == '__main__': 68 | main() 69 | -------------------------------------------------------------------------------- /source/chapter11/rscrape1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/rscrape1.py 4 | # Recursive scraper built using the Requests library. 5 | 6 | import argparse, requests 7 | from urllib.parse import urljoin, urlsplit 8 | from lxml import etree 9 | 10 | def GET(url): 11 | response = requests.get(url) 12 | if response.headers.get('Content-Type', '').split(';')[0] != 'text/html': 13 | return 14 | text = response.text 15 | try: 16 | html = etree.HTML(text) 17 | except Exception as e: 18 | print(' {}: {}'.format(e.__class__.__name__, e)) 19 | return 20 | links = html.findall('.//a[@href]') 21 | for link in links: 22 | yield GET, urljoin(url, link.attrib['href']) 23 | 24 | def scrape(start, url_filter): 25 | further_work = {start} 26 | already_seen = {start} 27 | while further_work: 28 | call_tuple = further_work.pop() 29 | function, url, *etc = call_tuple 30 | print(function.__name__, url, *etc) 31 | for call_tuple in function(url, *etc): 32 | if call_tuple in already_seen: 33 | continue 34 | already_seen.add(call_tuple) 35 | function, url, *etc = call_tuple 36 | if not url_filter(url): 37 | continue 38 | further_work.add(call_tuple) 39 | 40 | def main(GET): 41 | parser = argparse.ArgumentParser(description='Scrape a simple site.') 42 | parser.add_argument('url', help='the URL at which to begin') 43 | start_url = parser.parse_args().url 44 | starting_netloc = urlsplit(start_url).netloc 45 | url_filter = (lambda url: urlsplit(url).netloc == starting_netloc) 46 | scrape((GET, start_url), url_filter) 47 | 48 | if __name__ == '__main__': 49 | main(GET) 50 | -------------------------------------------------------------------------------- /source/chapter11/rscrape2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter11/rscrape2.py 4 | # Recursive scraper built using the Selenium Webdriver. 5 | 6 | from urllib.parse import urljoin 7 | from rscrape1 import main 8 | from selenium import webdriver 9 | 10 | class WebdriverVisitor: 11 | def __init__(self): 12 | self.browser = webdriver.Firefox() 13 | 14 | def GET(self, url): 15 | self.browser.get(url) 16 | yield from self.parse() 17 | if self.browser.find_elements_by_xpath('.//form'): 18 | yield self.submit_form, url 19 | 20 | def parse(self): 21 | # (Could also parse page.source with lxml yourself, as in scraper1.py) 22 | url = self.browser.current_url 23 | links = self.browser.find_elements_by_xpath('.//a[@href]') 24 | for link in links: 25 | yield self.GET, urljoin(url, link.get_attribute('href')) 26 | 27 | def submit_form(self, url): 28 | self.browser.get(url) 29 | self.browser.find_element_by_xpath('.//form').submit() 30 | yield from self.parse() 31 | 32 | if __name__ == '__main__': 33 | main(WebdriverVisitor().GET) 34 | -------------------------------------------------------------------------------- /source/chapter11/static/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | text-align: center; 3 | } 4 | body { 5 | display: inline-block; 6 | } 7 | .flash_message { 8 | position: relative; 9 | padding: 1em; 10 | color: white; 11 | background: green; 12 | } 13 | .flash_message a { 14 | position: absolute; 15 | top: 0px; 16 | right: 0px; 17 | margin-left: 0.5em; 18 | padding: 0.3em; 19 | color: white; 20 | text-decoration: none; 21 | font-family: sans-serif; 22 | font-weight: bold; 23 | } 24 | .complaint { 25 | display: block; 26 | padding-bottom: 1em; 27 | color: red; 28 | } 29 | ul { 30 | padding: 0px; 31 | text-align: left; 32 | } 33 | li { 34 | display: block; 35 | margin: 2px; 36 | padding: 2px 4px; 37 | } 38 | li.from { 39 | background-color: lightgreen; 40 | } 41 | li.to { 42 | background-color: pink; 43 | } 44 | form { 45 | display: block; 46 | float: left; 47 | clear: both; 48 | margin-left: auto; 49 | margin-right: auto; 50 | padding: 1em; 51 | box-shadow: 10px 10px 5px #aaa; 52 | border: 1px solid black; 53 | } 54 | label { 55 | display: block; 56 | text-align: center; 57 | } 58 | -------------------------------------------------------------------------------- /source/chapter11/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |Your Payments
8 |2 | This paragraph element includes a literal inline link to 3 | page 5 and another link to 4 | page 6. 5 |
6 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |This page includes several different kinds of link.
9 |11 | This paragraph element includes a literal inline link to 12 | page 1 and another link to 13 | page 2. 14 |
15 |17 | There is a search that this site can return: 18 |
22 | 23 |25 | The following div element is populated through a callback. 26 |
27 | 28 | 40 |41 | The first four words of this final paragraph are 42 | an anchor element that has no href attribute because it 43 | exists only to provide a name by which this paragraph can be 44 | referenced from a URL fragment — not to point anywhere else. 45 |
46 | 47 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/page1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Welcome to page 1.
8 | 9 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/page2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Welcome to page 2.
8 | 9 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/page3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Welcome to page 3.
8 | 9 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/page4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Welcome to page 4.
8 | 9 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/page5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Welcome to page 5.
8 | 9 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/page6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Welcome to page 6.
8 | 9 | -------------------------------------------------------------------------------- /source/chapter11/tinysite/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Here are your search results:
8 | 12 | 13 | -------------------------------------------------------------------------------- /source/chapter12/attachment.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Apress/foundations-of-python-network-programming-14/742320235e7a62854b7cbb4b21a4b1b3c6e07c08/source/chapter12/attachment.gz -------------------------------------------------------------------------------- /source/chapter12/attachment.txt: -------------------------------------------------------------------------------- 1 | This is a test 2 | -------------------------------------------------------------------------------- /source/chapter12/build_basic_email.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Foundations of Python Network Programming, Third Edition 3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter12/build_basic_email.py 4 | 5 | import email.message, email.policy, email.utils, sys 6 | 7 | text = """Hello, 8 | This is a basic message from Chapter 12. 9 | - Anonymous""" 10 | 11 | def main(): 12 | message = email.message.EmailMessage(email.policy.SMTP) 13 | message['To'] = 'recipient@example.com' 14 | message['From'] = 'Test SenderHello,
12 |This is a test message from Chapter 12.
13 |- Anonymous
""" 14 | 15 | img = """This is the smallest possible blue GIF:
16 |26 | This is a great test message from Chapter 12. I hope you enjoy 27 | it!
28 | -- Anonymous"""
29 |
30 |
31 | msg = MIMEMultipart('alternative')
32 | msg['To'] = 'recipient@example.com'
33 | msg['From'] = 'Test Sender
37 | This is a great test message from Chapter 12. I hope you enjoy
38 | it!
39 | -- Anonymous"""
40 |
41 | msg = MIMEMultipart()
42 | msg['To'] = 'recipient@example.com'
43 | msg['From'] = 'Test Sender
SMTP
4 |
5 | This is a directory of program listings from Chapter 13 of the book:
6 |
7 |
8 |
14 |
15 | You can learn more about the book by visiting the
16 | [root of this GitHub source code repository](https://github.com/brandon-rhodes/fopnp#readme).
17 |
18 | These scripts were written for Python 3, but can also run successfully
19 | under Python 2. Simply use [3to2](https://pypi.python.org/pypi/3to2) to
20 | convert them to the older syntax.
21 |
22 | The scripts in this chapter are best exercised inside the network
23 | [Playground](../../playground#readme) where `mail.example.com` is
24 | already set up and configured to receive incoming email. Once the
25 | playground is running, ask for a prompt on the `h1` host and visit the
26 | `chapter13` directory:
27 |
28 | $ ./play.sh h1
29 |
30 | # cd py3/chapter13
31 |
32 | At the `h1` machine’s prompt you can then experiment with sending
33 | messages across the network. The `simple.py` script and the slightly
34 | more advanced `ehlo.py` do their work silently, while `debug.py` asks
35 | the Standard Library to show the communication that is going on at the
36 | socket level.
37 |
38 | ```
39 | $ python3 simple.py mail.example.com sender@example.com brandon@example.com
40 | Message sent to 1 recipient
41 | ```
42 |
43 | ```
44 | $ python3 ehlo.py mail.example.com sender@example.com brandon@example.com
45 | Maximum message size is 10240000
46 | Message sent to 1 recipient
47 | ```
48 |
49 | ```
50 | $ python3 debug.py mail.example.com sender@example.com brandon@example.com
51 | send: 'ehlo [172.17.0.10]\r\n'
52 | reply: b'250-mail.example.com\r\n'
53 | reply: b'250-PIPELINING\r\n'
54 | reply: b'250-SIZE 10240000\r\n'
55 | reply: b'250-VRFY\r\n'
56 | reply: b'250-ETRN\r\n'
57 | reply: b'250-STARTTLS\r\n'
58 | reply: b'250-ENHANCEDSTATUSCODES\r\n'
59 | reply: b'250-8BITMIME\r\n'
60 | reply: b'250 DSN\r\n'
61 | reply: retcode (250); Msg: b'mail.example.com\nPIPELINING\nSIZE 10240000\nVRFY\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN'
62 | send: 'mail FROM:
11 | by Brandon Rhodes and John Goerzen
12 |
POP
4 |
5 | This is a directory of program listings from Chapter 14 of the book:
6 |
7 |
8 |
14 |
15 | You can learn more about the book by visiting the
16 | [root of this GitHub source code repository](https://github.com/brandon-rhodes/fopnp#readme).
17 |
18 | These scripts were written for Python 3, but can also run successfully
19 | under Python 2. Simply use [3to2](https://pypi.python.org/pypi/3to2) to
20 | convert them to the older syntax.
21 |
22 | You should probably never use the scripts in this chapter, as the POP
23 | protocol is unreliable, poorly designed and implemented on servers, and
24 | should be abandoned in favor of IMAP. See the chapter for details.
25 |
26 | The scripts in this chapter are best exercised inside the network
27 | [Playground](../../playground#readme) where `mail.example.com` is
28 | already set up and configured for POP. Once the playground is running,
29 | ask for a prompt on the `h1` host and visit this chapter’s directory:
30 |
31 | $ ./play.sh h1
32 |
33 | # cd py3/chapter14
34 |
35 | All of the scripts in this chapter are careful to use the `POP3_SSL`
36 | class and therefore guarantee the use of TLS to protect the user’s
37 | password and prevent other people in the same coffee shop from seeing
38 | the user’s email. The `popconn.py` script simply connects and reports
39 | the number of messages waiting:
40 |
41 | ```
42 | $ python popconn.py mail.example.com brandon
43 | Password: abc123
44 | You have 6 messages totaling 3441 bytes
45 | ```
46 |
47 | The `apopconn.py` script does exactly the same thing, but using a
48 | variant of the standard authentication methods. The `mailbox.py` script
49 | asks the server for a list of the messages that are waiting, and prints
50 | a brief summary about each one.
51 |
52 | ```
53 | $ python mailbox.py mail.example.com brandon
54 | Password: abc123
55 | Message 1 has 354 bytes
56 | Message 2 has 442 bytes
57 | Message 3 has 1175 bytes
58 | Message 4 has 491 bytes
59 | Message 5 has 490 bytes
60 | Message 6 has 489 bytes
61 | ```
62 |
63 | Finally, the `download_and_delete.py` script lets the user interactively
64 | view each message and decide whether to ask the server to delete it.
65 |
--------------------------------------------------------------------------------
/source/chapter14/apopconn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter14/apopconn.py
4 |
5 | import getpass, poplib, sys
6 |
7 | def main():
8 | if len(sys.argv) != 3:
9 | print('usage: %s hostname username' % sys.argv[0])
10 | exit(2)
11 |
12 | hostname, username = sys.argv[1:]
13 | passwd = getpass.getpass()
14 |
15 | p = poplib.POP3_SSL(hostname) # or "POP3" if SSL is not supported
16 | try:
17 | p.apop(username, passwd)
18 | except poplib.error_proto as e:
19 | print("Login failed:", e)
20 | else:
21 | status = p.stat()
22 | print("You have %d messages totaling %d bytes" % status)
23 | finally:
24 | p.quit()
25 |
26 | if __name__ == '__main__':
27 | main()
28 |
--------------------------------------------------------------------------------
/source/chapter14/download-and-delete.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter14/download-and-delete.py
4 |
5 | import email, getpass, poplib, sys
6 |
7 | def main():
8 | if len(sys.argv) != 3:
9 | print('usage: %s hostname username' % sys.argv[0])
10 | exit(2)
11 |
12 | hostname, username = sys.argv[1:]
13 | passwd = getpass.getpass()
14 |
15 | p = poplib.POP3_SSL(hostname)
16 | try:
17 | p.user(username)
18 | p.pass_(passwd)
19 | except poplib.error_proto as e:
20 | print("Login failed:", e)
21 | else:
22 | visit_all_listings(p)
23 | finally:
24 | p.quit()
25 |
26 | def visit_all_listings(p):
27 | response, listings, octets = p.list()
28 | for listing in listings:
29 | visit_listing(p, listing)
30 |
31 | def visit_listing(p, listing):
32 | number, size = listing.decode('ascii').split()
33 | print('Message', number, '(size is', size, 'bytes):')
34 | print()
35 | response, lines, octets = p.top(number, 0)
36 | document = '\n'.join( line.decode('ascii') for line in lines )
37 | message = email.message_from_string(document)
38 | for header in 'From', 'To', 'Subject', 'Date':
39 | if header in message:
40 | print(header + ':', message[header])
41 | print()
42 | print('Read this message [ny]?')
43 | answer = input()
44 | if answer.lower().startswith('y'):
45 | response, lines, octets = p.retr(number)
46 | document = '\n'.join( line.decode('ascii') for line in lines )
47 | message = email.message_from_string(document)
48 | print('-' * 72)
49 | for part in message.walk():
50 | if part.get_content_type() == 'text/plain':
51 | print(part.get_payload())
52 | print('-' * 72)
53 | print()
54 | print('Delete this message [ny]?')
55 | answer = input()
56 | if answer.lower().startswith('y'):
57 | p.dele(number)
58 | print('Deleted.')
59 |
60 | if __name__ == '__main__':
61 | main()
62 |
--------------------------------------------------------------------------------
/source/chapter14/mailbox.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter14/mailbox.py
4 |
5 | import getpass, poplib, sys
6 |
7 | def main():
8 | if len(sys.argv) != 3:
9 | print('usage: %s hostname username' % sys.argv[0])
10 | exit(2)
11 |
12 | hostname, username = sys.argv[1:]
13 | passwd = getpass.getpass()
14 |
15 | p = poplib.POP3_SSL(hostname)
16 | try:
17 | p.user(username)
18 | p.pass_(passwd)
19 | except poplib.error_proto as e:
20 | print("Login failed:", e)
21 | else:
22 | response, listings, octet_count = p.list()
23 | if not listings:
24 | print("No messages")
25 | for listing in listings:
26 | number, size = listing.decode('ascii').split()
27 | print("Message %s has %s bytes" % (number, size))
28 | finally:
29 | p.quit()
30 |
31 | if __name__ == '__main__':
32 | main()
33 |
--------------------------------------------------------------------------------
/source/chapter14/popconn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter14/popconn.py
4 |
5 | import getpass, poplib, sys
6 |
7 | def main():
8 | if len(sys.argv) != 3:
9 | print('usage: %s hostname username' % sys.argv[0])
10 | exit(2)
11 |
12 | hostname, username = sys.argv[1:]
13 | passwd = getpass.getpass()
14 |
15 | p = poplib.POP3_SSL(hostname) # or "POP3" if SSL is not supported
16 | try:
17 | p.user(username)
18 | p.pass_(passwd)
19 | except poplib.error_proto as e:
20 | print("Login failed:", e)
21 | else:
22 | status = p.stat()
23 | print("You have %d messages totaling %d bytes" % status)
24 | finally:
25 | p.quit()
26 |
27 | if __name__ == '__main__':
28 | main()
29 |
--------------------------------------------------------------------------------
/source/chapter15/README.md:
--------------------------------------------------------------------------------
1 | [Return to the Table of Contents](https://github.com/brandon-rhodes/fopnp#readme)
2 |
3 | # Chapter 15
11 | by Brandon Rhodes and John Goerzen
12 |
IMAP
4 |
5 | This is a directory of program listings from Chapter 15 of the book:
6 |
7 |
8 |
14 |
15 | You can learn more about the book by visiting the
16 | [root of this GitHub source code repository](https://github.com/brandon-rhodes/fopnp#readme).
17 |
18 | These scripts were written for Python 3, but can also run successfully
19 | under Python 2. Simply use [3to2](https://pypi.python.org/pypi/3to2) to
20 | convert them to the older syntax.
21 |
22 | The scripts in this chapter are best exercised inside the network
23 | [Playground](../../playground#readme) where `mail.example.com` is
24 | already set up and configured for POP. Once the playground is running,
25 | ask for a prompt on the `h1` host and visit this chapter’s directory:
26 |
27 | $ ./play.sh h1
28 |
29 | # cd py3/chapter14
30 |
31 | The scripts will all need the password of the `brandon` user, which is
32 | `abc123` and in the following examples will be piped in with the `echo`
33 | command. One lone script, `open_maplib.py`, uses the Standard Library
34 | `imaplib` module:
35 |
36 | ```
37 | $ echo abc123 | python3 open_imaplib.py mail.example.com brandon
38 | Capabilities: ('IMAP4REV1', 'LITERAL+', 'SASL-IR', 'LOGIN-REFERRALS',
39 | 'ID', 'ENABLE', 'IDLE', 'AUTH=PLAIN')
40 | Listing mailboxes
41 | Status: 'OK'
42 | Data:
43 | b'(\\HasNoChildren) "/" INBOX'
44 | ```
45 |
46 | All of the other scripts, for technical issues explained in the chapter,
47 | use the third party `imapclient` module by Menno Smits. It is both
48 | capable of negotiating additional capabilities with the server, and also
49 | parses the list of mailboxes on its own.
50 |
51 | ```
52 | $ echo abc123 | python3 open_imap.py mail.example.com brandon
53 | Capabilities: ('IMAP4REV1', 'LITERAL+', 'SASL-IR', 'LOGIN-REFERRALS',
54 | 'ID', 'ENABLE', 'IDLE', 'SORT', 'SORT=DISPLAY', 'THREAD=REFERENCES',
55 | 'THREAD=REFS', 'THREAD=ORDEREDSUBJECT', 'MULTIAPPEND', 'URL-PARTIAL',
56 | 'CATENATE', 'UNSELECT', 'CHILDREN', 'NAMESPACE', 'UIDPLUS',
57 | 'LIST-EXTENDED', 'I18NLEVEL=1', 'CONDSTORE', 'QRESYNC', 'ESEARCH',
58 | 'ESORT', 'SEARCHRES', 'WITHIN', 'CONTEXT=SEARCH', 'LIST-STATUS',
59 | 'SPECIAL-USE', 'BINARY', 'MOVE')
60 | Listing mailboxes:
61 | \HasNoChildren / INBOX
62 | ```
63 |
64 | Two further scripts show how to pull basic information about a folder,
65 | and how to provide a full summary of the messages inside.
66 |
67 | ```
68 | $ echo abc123 | python3 folder_info.py mail.example.com brandon INBOX
69 | EXISTS: 3
70 | FLAGS: ('\\Answered', '\\Flagged', '\\Deleted', '\\Seen', '\\Draft')
71 | NOMODSEQ: ['']
72 | PERMANENTFLAGS: ()
73 | READ-ONLY: ['']
74 | RECENT: 0
75 | UIDNEXT: 11
76 | UIDVALIDITY: 1414010141
77 | UNSEEN: ['1']
78 | ```
79 |
80 | ```
81 | $ echo abc123 | python3 folder_summary.py mail.example.com brandon INBOX
82 | 1 Administrator
11 | by Brandon Rhodes and John Goerzen
12 |
Telnet and SSH
4 |
5 | This is a directory of program listings from Chapter 16 of the book:
6 |
7 |
8 |
14 |
15 | You can learn more about the book by visiting the
16 | [root of this GitHub source code repository](https://github.com/brandon-rhodes/fopnp#readme).
17 |
18 | These scripts were written for Python 3, but can also run successfully
19 | under Python 2. Simply use [3to2](https://pypi.python.org/pypi/3to2) to
20 | convert them to the older syntax.
21 |
22 | Before diving into Telnet and SSH, the chapter introduces a small shell
23 | program `shell.py` that interprets nothing but whitespace as special on
24 | the command line, in an attempt to demonstrate to the skeptical reader
25 | that all of the characters they are used to treating as special are in
26 | fact quite ordinary down at the level of the operating system.
27 |
28 | Once the reader has been instructed as to the role of command lines, the
29 | features of terminals, and the pitfalls of special characters and
30 | quoting, two scripts are introduced which use the old insecure Telnet
31 | protocol. They can be exercised in the Playground against the
32 | `ftp.example.com` server which, just for fun, has a Telnet server
33 | running as well.
34 |
35 | ```
36 | $ python telnet_login.py ftp.example.com brandon
37 | exec uptime
38 | 21:18:15 up 5:40, 1 user, load average: 0.00, 0.02, 0.05
39 | ```
40 |
41 | ```
42 | $ python telnet_codes.py ftp.example.com brandon
43 | Sending terminal type "mypython"
44 | ('Will not', 32)
45 | ('Will not', 35)
46 | ('Will not', 39)
47 | ('Do not', 3)
48 | ('Will not', 1)
49 | ('Will not', 31)
50 | ('Do not', 5)
51 | ('Will not', 33)
52 | ('Do not', 3)
53 | ('Do not', 1)
54 | exec echo My terminal type is $TERM
55 | My terminal type is mypython
56 | ```
57 |
58 | When running the SSH scripts, you can target any of the hosts running in
59 | the Playground — they are all running SSH and should have both a `root`
60 | user and `brandon` user. For the examples below we will use the latter.
61 | To avoid having to edit the program listings to provide a password
62 | argument to `client.connect()`, ask SSH to install the `root` identity
63 | under the `brandon` account to which you wish to connect. Because
64 | Docker does not launch your initial shell on a host with everything set
65 | up as though you had logged in, this will only work if you `su` to the
66 | `root` user first:
67 |
68 | $ ./play.sh h1
69 |
70 | # su root
71 |
72 | # ssh-copy-id brandon@www.example.com
73 |
74 | By entering the `brandon` password `abc123` the ID should be copied
75 | successfully and a plain SSH command should then succeed without a
76 | password:
77 |
78 | # ssh brandon@www.example.com echo Success
79 | Success
80 |
81 | Once `ssh` itself is working without a password, the Python scripts
82 | should succeed as well.
83 |
84 | ```
85 | $ python3 ssh_simple.py www.example.com brandon
86 | Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-24-generic x86_64)
87 |
88 | * Documentation: https://help.ubuntu.com/
89 | Last login: Wed Oct 22 21:38:21 2014 from modema
90 | echo Hello, world
91 | exit
92 | $ Hello, world
93 | $
94 | ```
95 |
96 | As you can see, trying to speak over a single communications channel to
97 | both a shell and the programs it invokes is something of a disaster.
98 | The book explains a better approach:
99 |
100 | ```
101 | $ python3 ssh_commands.py www.example.com brandon
102 | b'Hello, world!\n'
103 | b'Linux\n'
104 | b' 21:38:33 up 6:00, 0 users, load average: 0.16, 0.05, 0.06\n'
105 | ```
106 |
107 | The ability of SSH to support several channels even allows multiple
108 | Python threads to have remove commands running at the same time.
109 |
110 | ```
111 | $ python3 ssh_threads.py www.example.com brandon
112 | One
113 | A
114 | B
115 | Two
116 | Three
117 | C
118 | ```
119 |
120 | Finally, SSH includes a built-in file transfer protocol SFTP.
121 |
122 | ```
123 | $ python3 sftp_get.py www.example.com brandon /etc/lsb-release
124 | Transfer of '/etc/lsb-release' is at 105/105 bytes (100.0%)
125 | Transfer of '/etc/lsb-release' is at 105/105 bytes (100.0%)
126 | ```
127 |
128 |
--------------------------------------------------------------------------------
/source/chapter16/sftp_get.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/sftp_get.py
4 | # Fetching files with SFTP
5 |
6 | import argparse, functools, paramiko
7 |
8 | class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):
9 | def missing_host_key(self, client, hostname, key):
10 | return
11 |
12 | def main(hostname, username, filenames):
13 | client = paramiko.SSHClient()
14 | client.set_missing_host_key_policy(AllowAnythingPolicy())
15 | client.connect(hostname, username=username) # password='')
16 |
17 | def print_status(filename, bytes_so_far, bytes_total):
18 | percent = 100. * bytes_so_far / bytes_total
19 | print('Transfer of %r is at %d/%d bytes (%.1f%%)' % (
20 | filename, bytes_so_far, bytes_total, percent))
21 |
22 | sftp = client.open_sftp()
23 | for filename in filenames:
24 | if filename.endswith('.copy'):
25 | continue
26 | callback = functools.partial(print_status, filename)
27 | sftp.get(filename, filename + '.copy', callback=callback)
28 | client.close()
29 |
30 | if __name__ == '__main__':
31 | parser = argparse.ArgumentParser(description='Copy files over SSH')
32 | parser.add_argument('hostname', help='Remote machine name')
33 | parser.add_argument('username', help='Username on the remote machine')
34 | parser.add_argument('filename', nargs='+', help='Filenames to fetch')
35 | args = parser.parse_args()
36 | main(args.hostname, args.username, args.filename)
37 |
--------------------------------------------------------------------------------
/source/chapter16/shell.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/shell.py
4 | # A simple shell, so you can try running commands at a prompt where no
5 | # characters are special (except that whitespace separates arguments).
6 |
7 | import subprocess
8 |
9 | def main():
10 | while True:
11 | args = input('] ').strip().split()
12 | if not args:
13 | pass
14 | elif args == ['exit']:
15 | break
16 | elif args[0] == 'show':
17 | print("Arguments:", args[1:])
18 | else:
19 | try:
20 | subprocess.call(args)
21 | except Exception as e:
22 | print(e)
23 |
24 | if __name__ == '__main__':
25 | main()
26 |
--------------------------------------------------------------------------------
/source/chapter16/ssh_commands.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/ssh_commands.py
4 | # Running three separate commands, and reading three separate outputs
5 |
6 | import argparse, paramiko
7 |
8 | class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):
9 | def missing_host_key(self, client, hostname, key):
10 | return
11 |
12 | def main(hostname, username):
13 | client = paramiko.SSHClient()
14 | client.set_missing_host_key_policy(AllowAnythingPolicy())
15 | client.connect(hostname, username=username) # password='')
16 |
17 | for command in 'echo "Hello, world!"', 'uname', 'uptime':
18 | stdin, stdout, stderr = client.exec_command(command)
19 | stdin.close()
20 | print(repr(stdout.read()))
21 | stdout.close()
22 | stderr.close()
23 |
24 | client.close()
25 |
26 | if __name__ == '__main__':
27 | parser = argparse.ArgumentParser(description='Connect over SSH')
28 | parser.add_argument('hostname', help='Remote machine name')
29 | parser.add_argument('username', help='Username on the remote machine')
30 | args = parser.parse_args()
31 | main(args.hostname, args.username)
32 |
--------------------------------------------------------------------------------
/source/chapter16/ssh_simple.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/ssh_simple.py
4 | # Using SSH like Telnet: connecting and running two commands
5 |
6 | import argparse, paramiko, sys
7 |
8 | class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):
9 | def missing_host_key(self, client, hostname, key):
10 | return
11 |
12 | def main(hostname, username):
13 | client = paramiko.SSHClient()
14 | client.set_missing_host_key_policy(AllowAnythingPolicy())
15 | client.connect(hostname, username=username) # password='')
16 |
17 | channel = client.invoke_shell()
18 | stdin = channel.makefile('wb')
19 | stdout = channel.makefile('rb')
20 |
21 | stdin.write(b'echo Hello, world\rexit\r')
22 | output = stdout.read()
23 | client.close()
24 |
25 | sys.stdout.buffer.write(output)
26 |
27 | if __name__ == '__main__':
28 | parser = argparse.ArgumentParser(description='Connect over SSH')
29 | parser.add_argument('hostname', help='Remote machine name')
30 | parser.add_argument('username', help='Username on the remote machine')
31 | args = parser.parse_args()
32 | main(args.hostname, args.username)
33 |
--------------------------------------------------------------------------------
/source/chapter16/ssh_threads.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/ssh_threads.py
4 | # Running two remote commands simultaneously in different channels
5 |
6 | import argparse, paramiko, threading
7 |
8 | class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):
9 | def missing_host_key(self, client, hostname, key):
10 | return
11 |
12 | def main(hostname, username):
13 | client = paramiko.SSHClient()
14 | client.set_missing_host_key_policy(AllowAnythingPolicy())
15 | client.connect(hostname, username=username) # password='')
16 |
17 | def read_until_EOF(fileobj):
18 | s = fileobj.readline()
19 | while s:
20 | print(s.strip())
21 | s = fileobj.readline()
22 |
23 | ioe1 = client.exec_command('echo One;sleep 2;echo Two;sleep 1;echo Three')
24 | ioe2 = client.exec_command('echo A;sleep 1;echo B;sleep 2;echo C')
25 | thread1 = threading.Thread(target=read_until_EOF, args=(ioe1[1],))
26 | thread2 = threading.Thread(target=read_until_EOF, args=(ioe2[1],))
27 | thread1.start()
28 | thread2.start()
29 | thread1.join()
30 | thread2.join()
31 |
32 | client.close()
33 |
34 | if __name__ == '__main__':
35 | parser = argparse.ArgumentParser(description='Connect over SSH')
36 | parser.add_argument('hostname', help='Remote machine name')
37 | parser.add_argument('username', help='Username on the remote machine')
38 | args = parser.parse_args()
39 | main(args.hostname, args.username)
40 |
--------------------------------------------------------------------------------
/source/chapter16/telnet_codes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/telnet_codes.py
4 | # How your code might look if you intercept Telnet options yourself
5 |
6 | import argparse, getpass
7 | from telnetlib import Telnet, IAC, DO, DONT, WILL, WONT, SB, SE, TTYPE
8 |
9 | def process_option(tsocket, command, option):
10 | if command == DO and option == TTYPE:
11 | tsocket.sendall(IAC + WILL + TTYPE)
12 | print('Sending terminal type "mypython"')
13 | tsocket.sendall(IAC + SB + TTYPE + b'\0' + b'mypython' + IAC + SE)
14 | elif command in (DO, DONT):
15 | print('Will not', ord(option))
16 | tsocket.sendall(IAC + WONT + option)
17 | elif command in (WILL, WONT):
18 | print('Do not', ord(option))
19 | tsocket.sendall(IAC + DONT + option)
20 |
21 | def main(hostname, username, password):
22 | t = Telnet(hostname)
23 | # t.set_debuglevel(1) # uncomment to get debug messages
24 | t.set_option_negotiation_callback(process_option)
25 | t.read_until(b'login:', 10)
26 | t.write(username.encode('utf-8') + b'\r')
27 | t.read_until(b'assword:', 10) # first letter might be 'p' or 'P'
28 | t.write(password.encode('utf-8') + b'\r')
29 | n, match, previous_text = t.expect([br'Login incorrect', br'\$'], 10)
30 | if n == 0:
31 | print("Username and password failed - giving up")
32 | else:
33 | t.write(b'exec echo My terminal type is $TERM\n')
34 | print(t.read_all().decode('ascii'))
35 |
36 | if __name__ == '__main__':
37 | parser = argparse.ArgumentParser(description='Use Telnet to log in')
38 | parser.add_argument('hostname', help='Remote host to telnet to')
39 | parser.add_argument('username', help='Remote username')
40 | args = parser.parse_args()
41 | password = getpass.getpass()
42 | main(args.hostname, args.username, password)
43 |
--------------------------------------------------------------------------------
/source/chapter16/telnet_login.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter16/telnet_login.py
4 | # Connect to localhost, watch for a login prompt, and try logging in
5 |
6 | import argparse, getpass, telnetlib
7 |
8 | def main(hostname, username, password):
9 | t = telnetlib.Telnet(hostname)
10 | # t.set_debuglevel(1) # uncomment to get debug messages
11 | t.read_until(b'login:')
12 | t.write(username.encode('utf-8'))
13 | t.write(b'\r')
14 | t.read_until(b'assword:') # first letter might be 'p' or 'P'
15 | t.write(password.encode('utf-8'))
16 | t.write(b'\r')
17 | n, match, previous_text = t.expect([br'Login incorrect', br'\$'], 10)
18 | if n == 0:
19 | print('Username and password failed - giving up')
20 | else:
21 | t.write(b'exec uptime\r')
22 | print(t.read_all().decode('utf-8')) # read until socket closes
23 |
24 | if __name__ == '__main__':
25 | parser = argparse.ArgumentParser(description='Use Telnet to log in')
26 | parser.add_argument('hostname', help='Remote host to telnet to')
27 | parser.add_argument('username', help='Remote username')
28 | args = parser.parse_args()
29 | password = getpass.getpass('Password: ')
30 | main(args.hostname, args.username, password)
31 |
--------------------------------------------------------------------------------
/source/chapter17/README.md:
--------------------------------------------------------------------------------
1 | [Return to the Table of Contents](https://github.com/brandon-rhodes/fopnp#readme)
2 |
3 | # Chapter 17
11 | by Brandon Rhodes and John Goerzen
12 |
FTP
4 |
5 | This is a directory of program listings from Chapter 17 of the book:
6 |
7 |
8 |
14 |
15 | You can learn more about the book by visiting the
16 | [root of this GitHub source code repository](https://github.com/brandon-rhodes/fopnp#readme).
17 |
18 | These scripts were written for Python 3, but can also run successfully
19 | under Python 2. Simply use [3to2](https://pypi.python.org/pypi/3to2) to
20 | convert them to the older syntax.
21 |
22 | Most of the scripts in this chapter are for searching and downloading
23 | from FTP sites, and are hard-wired to run against well-known public
24 | servers that should give predictable results.
25 |
26 | ```
27 | $ python3 connect.py
28 | Welcome: 220 ProFTPD Server
29 | Current working directory: /
30 | ```
31 |
32 | ```
33 | $ python3 nlst.py
34 | 13 entries:
35 | INDEX
36 | README
37 | ephem_4.28.tar.Z
38 | hawaii_scope
39 | incoming
40 | jupitor-moons.shar.Z
41 | lunar.c.Z
42 | lunisolar.shar.Z
43 | moon.shar.Z
44 | planetary
45 | sat-track.tar.Z
46 | stars.tar.Z
47 | xephem.tar.Z
48 | ```
49 |
50 | ```
51 | $ python3 dir.py
52 | 13 entries:
53 | -rw-r--r-- 1 48 25 341303 Oct 2 1992 ephem_4.28.tar.Z
54 | drwxr-xr-x 2 48 25 4096 Feb 11 1999 hawaii_scope
55 | drwxr-xr-x 2 48 utempter 4096 Feb 11 1999 incoming
56 | -rw-r--r-- 1 48 25 750 Feb 14 1994 INDEX
57 | -rw-r--r-- 1 48 25 5983 Oct 2 1992 jupitor-moons.shar.Z
58 | -rw-r--r-- 1 48 25 1751 Oct 2 1992 lunar.c.Z
59 | -rw-r--r-- 1 48 25 8078 Oct 2 1992 lunisolar.shar.Z
60 | -rw-r--r-- 1 48 25 64209 Oct 2 1992 moon.shar.Z
61 | drwxr-xr-x 2 48 25 4096 Jan 6 1993 planetary
62 | -rw-r--r-- 1 root bin 135 Feb 11 1999 README
63 | -rw-r--r-- 1 48 25 129969 Oct 2 1992 sat-track.tar.Z
64 | -rw-r--r-- 1 48 25 16504 Oct 2 1992 stars.tar.Z
65 | -rw-r--r-- 1 48 25 410650 Oct 2 1992 xephem.tar.Z
66 | ```
67 |
68 | The simple download files are silent while doing their work, and you
69 | will have to list the current directory contents later to see that they
70 | had any effect. The advanced download scripts, by contrast, should
71 | constantly update the screen as you run them to report on progress.
72 |
73 | ```
74 | $ python3 asciidl.py
75 | ```
76 |
77 | ```
78 | $ python3 binarydl.py
79 | ```
80 |
81 | ```
82 | $ python3 advbinarydl.py
83 | Received 1448 of 1259161 total bytes (0.1%)
84 | ...
85 | Received 1259161 of 1259161 total bytes (100.0%)
86 | ```
87 |
88 | ```
89 | $ python3 recursedl.py
90 | /pub/linux/kernel/Historic/old-versions
91 | /pub/linux/kernel/Historic/old-versions/impure
92 | /pub/linux/kernel/Historic/old-versions/old
93 | /pub/linux/kernel/Historic/old-versions/old/corrupt
94 | /pub/linux/kernel/Historic/old-versions/tytso
95 | ```
96 |
97 | Finally, the two scripts for doing binary uploads are best run from
98 | inside of the [Playground](../../playground#readme) because it comes
99 | with a host named `ftp.example.com` that already has an FTP server
100 | installed. Try creating the `h1` host and moving into the `chapter17`
101 | directory:
102 |
103 | $ ./play h1
104 |
105 | # cd py3/chapter17
106 |
107 | The username `brandon` and the password `abc123` should let you upload
108 | binary files to the user’s home directory:
109 |
110 | ```
111 | $ python3 binaryul.py ftp.example.com brandon /bin/true .
112 | Enter password for brandon on ftp.example.com: abc123
113 | ```
114 |
115 | ```
116 | $ python3 advbinaryul.py ftp.example.com brandon /bin/false .
117 | Enter password for brandon on ftp.example.com: abc123
118 |
Sent 8192 of 27168 bytes (30.2%)
119 |
Sent 16384 of 27168 bytes (60.3%)
120 |
Sent 24576 of 27168 bytes (90.5%)
121 |
Sent 27168 of 27168 bytes (100.0%)
122 | ```
123 |
124 | A quick connection to the `ftp` host should then confirm that the files
125 | arrived successfully:
126 |
127 | # ssh brandon@ftp.example.com
128 | brandon@ftp.example.com's password: abc123
129 |
130 | Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-24-generic x86_64)
131 |
132 | $ ls -l
133 | total 56
134 | -rw------- 1 brandon brandon 27168 Oct 23 01:47 false
135 | -rw------- 1 brandon brandon 27168 Oct 23 01:47 true
136 |
--------------------------------------------------------------------------------
/source/chapter17/advbinarydl.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter17/advbinarydl.py
4 |
5 | import os, sys
6 | from ftplib import FTP
7 |
8 | def main():
9 | if os.path.exists('linux-1.0.tar.gz'):
10 | raise IOError('refusing to overwrite your linux-1.0.tar.gz file')
11 |
12 | ftp = FTP('ftp.kernel.org')
13 | ftp.login()
14 | ftp.cwd('/pub/linux/kernel/v1.0')
15 | ftp.voidcmd("TYPE I")
16 |
17 | socket, size = ftp.ntransfercmd("RETR linux-1.0.tar.gz")
18 | nbytes = 0
19 |
20 | f = open('linux-1.0.tar.gz', 'wb')
21 |
22 | while True:
23 | data = socket.recv(2048)
24 | if not data:
25 | break
26 | f.write(data)
27 | nbytes += len(data)
28 | print("\rReceived", nbytes, end=' ')
29 | if size:
30 | print("of %d total bytes (%.1f%%)"
31 | % (size, 100 * nbytes / float(size)), end=' ')
32 | else:
33 | print("bytes", end=' ')
34 | sys.stdout.flush()
35 |
36 | print()
37 | f.close()
38 | socket.close()
39 | ftp.voidresp()
40 | ftp.quit()
41 |
42 | if __name__ == '__main__':
43 | main()
44 |
--------------------------------------------------------------------------------
/source/chapter17/advbinaryul.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Foundations of Python Network Programming, Third Edition
3 | # https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter17/advbinaryul.py
4 |
5 | from ftplib import FTP
6 | import sys, getpass, os.path
7 |
8 | BLOCKSIZE = 8192 # chunk size to read and transmit: 8 kB
9 |
10 | def main():
11 | if len(sys.argv) != 5:
12 | print("usage:", sys.argv[0],
13 | "
11 | by Brandon Rhodes and John Goerzen
12 |
RPC
4 |
5 | This is a directory of program listings from Chapter 18 of the book:
6 |
7 |
8 |
14 |
15 | You can learn more about the book by visiting the
16 | [root of this GitHub source code repository](https://github.com/brandon-rhodes/fopnp#readme).
17 |
18 | These scripts were written for Python 3, but most of them will run under
19 | Python 2. Use [3to2](https://pypi.python.org/pypi/3to2) to convert them
20 | to the older syntax. The exception is that `xmlrpc_client.py` runs into
21 | trouble because it attempts to send Unicode strings in its RPC call, but
22 | the Standard Library `xmlrpclib` does not know how to encode them.
23 |
24 | The chapter explores three kinds of RPC: XML-RPC, JSON-RPC, and a
25 | Python-specific system called RPyC. The XML-RPC protocol is supported
26 | by the Standard Library, while the other two require third-party
27 | packages from the Python Package Index.
28 |
29 | In each case, the chapter provides both a small sample server and then a
30 | client that puts the server through its paces. As all of the examples
31 | are hard-coded to use `localhost`, they can all be safely run right on
32 | your machine.
33 |
34 | ## XML-RPC
35 |
36 | ```
37 | $ python3 xmlrpc_server.py &>server.log &
38 | ```
39 |
40 | ```
41 | $ python3 xmlrpc_client.py
42 | xÿz
43 | 55
44 | [0.0, 8.0]
45 | [-1.0]
46 | [1, 2.0, 'three']
47 | [1, 2.0, 'three']
48 | {'data': {'age': 42, 'sex': 'M'}, 'name': 'Arthur'}
49 | Traceback (most recent call last):
50 | ...
51 | xmlrpc.client.Fault:
11 | by Brandon Rhodes and John Goerzen
12 |