├── .gitignore ├── ss.jpg ├── pyproject.toml ├── LICENSE ├── .github └── workflows │ └── pypi.yml ├── README.md └── wififtp.py /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /ss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KasRoudra/wififtp/HEAD/ss.jpg -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=58.0.4", 4 | "wheel>=0.37.0" 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | 8 | [tool.poetry] 9 | name = "WiFiFTP" 10 | version = "0.1.1" 11 | readme = "README.md" 12 | license = "MIT" 13 | repository = "https://github.com/KasRoudra/WiFiFTP/" 14 | include = ["README.md", "LICENSE"] 15 | description = "Share files between devices connected to same wlan/wifi/hotspot/router" 16 | authors = ["KasRoudra "] 17 | 18 | [tool.poetry.dependencies] 19 | python = "^3.9" 20 | pyftpdlib = "^1.5.0" 21 | 22 | [tool.poetry.scripts] 23 | wififtp = "wififtp:main" 24 | 25 | [build-system.sdist] 26 | formats = ["gztar"] 27 | 28 | [build-system.bdist_wheel] 29 | universal = false 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2023 KasRoudra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | name: PyPI Publish 2 | 3 | # Controls when the action will run. 4 | on: 5 | push: 6 | branches: [ pypi ] 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job called "publish" 13 | publish: 14 | # The type of runner that the job will run on 15 | runs-on: ubuntu-latest 16 | 17 | # Steps represent a sequence of tasks that will be executed as part of the job 18 | steps: 19 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 20 | - uses: actions/checkout@v3 21 | 22 | # Sets up python 23 | - uses: actions/setup-python@v2 24 | with: 25 | python-version: 3.11 26 | # Install dependencies 27 | - name: "Installs dependencies" 28 | run: | 29 | python3 -m pip install --upgrade pip --break-system-packages 30 | python3 -m pip install setuptools wheel poetry --break-system-packages 31 | python3 -m pip install pyftpdlib --break-system-packages 32 | 33 | # Build and upload to PyPI 34 | - name: "Builds and uploads to PyPI" 35 | run: | 36 | python3 -m poetry build 37 | python3 -m poetry config pypi-token.pypi ${PYPI_TOKEN} 38 | python3 -m poetry publish 39 | env: 40 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WiFi-FTP 2 | 3 | ### [+] Description : 4 | ***WiFi-FTP is a tool to create a simple ftp server in local network. Anyone under same wifi/router can read/write/modify the folder you shared.*** 5 | 6 | 7 | ### [-] Installation 8 | 9 | ```apt install python3 python3-pip -y``` 10 | 11 | ```pip3 install pyftpdlib --break-system-packages``` 12 | 13 | ```git clone https://github.com/KasRoudra/WiFiFTP``` 14 | 15 | ```cd WiFiFTP``` 16 | 17 | ```python3 wififtp.py``` 18 | 19 | ### Pip 20 | - `pip3 install wififtp` [For Termux] 21 | - `sudo pip3 install wififtp --break-system-packages` [For Linux] 22 | - `wififtp` 23 | 24 | ### [*] Features 25 | - You can customize both port and shared folder. Without change, default port will be 2121 and default folder will be the folder from which the file is executed! 26 | - Now WiFi-FTP also support arguments 27 | 28 | ### [~] Options 29 | ``` 30 | usage: wififtp.py [-h] [-p PORT] [-d DIRECTORY] [-u USERNAME] [-k PASSWORD] 31 | [-v] 32 | 33 | options: 34 | -h, --help show this help message and exit 35 | -p PORT, --port PORT WiFiFTP's server port [Default: 2121] 36 | -d DIRECTORY, --directory DIRECTORY 37 | Directory where server will start [Default: ~/wififtp] 38 | -u USERNAME, --username USERNAME 39 | FTP Username [Default: None] 40 | -k PASSWORD, --password PASSWORD 41 | FTP Password [Default: None] 42 | -v, --version Prints version of WiFiFTP 43 | 44 | ``` 45 | ## [+] Caution: 46 | 47 | ### You must need "Python" installed in your operating system. If you use firewall you also have to enable python from "Windows Firewall". If you can't enable python from firewall, you may need to disable firewall while using ftp! 48 | ### Your sharing link will be like "ftp://192.168.0.105:2121". Make sure you are connected to same router/Wi-Fi to access folder from other device! 49 | ## [+] Screenshot: 50 | ![preview](https://github.com/KasRoudra/wififtp/raw/main/ss.jpg) 51 | 52 | ## [~] Find Me on : 53 | 54 | - [![Github](https://img.shields.io/badge/Github-KasRoudra-green?style=for-the-badge&logo=github)](https://github.com/KasRoudra) 55 | 56 | - [![Gmail](https://img.shields.io/badge/Gmail-KasRoudra-green?style=for-the-badge&logo=gmail)](mailto:kasroudrakrd@gmail.com) 57 | 58 | - [![Facebook](https://img.shields.io/badge/Facebook-KasRoudra-green?style=for-the-badge&logo=facebook)](https://facebook.com/KasRoudra) 59 | 60 | - [![Messenger](https://img.shields.io/badge/Messenger-KasRoudra-green?style=for-the-badge&logo=messenger)](https://m.me/KasRoudra) 61 | 62 | - [![Telegram](https://img.shields.io/badge/Telegram-KasRoudra-indigo?style=for-the-badge&logo=telegram)](https://t.me/KasRoudra) 63 | -------------------------------------------------------------------------------- /wififtp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # ToolName : WiFiFTP 3 | # Author : KasRoudra 4 | # License : MIT 5 | # Copyright : KasRoudra (2021-2023) 6 | # Github : https://github.com/KasRoudra 7 | # Contact : https://t.me/KasRoudra 8 | # Description: Share files between devices connected to same wlan/wifi/hotspot/router"" 9 | # Tags : ftp, wififtp, share-files 10 | # 1st Commit : 18/08/2021 11 | # Language : Python 12 | # Portable file/script 13 | # If you copy open source code, consider giving credit 14 | # Env : #!/usr/bin/env python 15 | 16 | """ 17 | MIT License 18 | 19 | Copyright (c) 2021-2023 KasRoudra 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | """ 39 | 40 | from argparse import ArgumentParser 41 | from os import ( 42 | name, 43 | getcwd, 44 | getenv 45 | ) 46 | from os.path import ( 47 | isfile, 48 | isdir, 49 | dirname 50 | ) 51 | from socket import ( 52 | socket, 53 | error, 54 | AF_INET, 55 | SOCK_DGRAM, 56 | SOCK_STREAM 57 | ) 58 | from subprocess import ( 59 | run, 60 | DEVNULL, 61 | PIPE, 62 | Popen 63 | ) 64 | from sys import ( 65 | executable, 66 | stdout 67 | ) 68 | from time import sleep 69 | from re import escape 70 | 71 | 72 | # Color snippets 73 | black = "\033[0;30m" 74 | red = "\033[0;31m" 75 | bred = "\033[1;31m" 76 | green = "\033[0;32m" 77 | bgreen = "\033[1;32m" 78 | yellow = "\033[0;33m" 79 | byellow = "\033[1;33m" 80 | blue = "\033[0;34m" 81 | bblue = "\033[1;34m" 82 | purple = "\033[0;35m" 83 | bpurple = "\033[1;35m" 84 | cyan = "\033[0;36m" 85 | bcyan = "\033[1;36m" 86 | white = "\033[0;37m" 87 | nc = "\033[00m" 88 | 89 | version = "0.1.1" 90 | 91 | # Major Minor version ignoring patch 92 | mm_ver = version[:3] 93 | 94 | # Regular Snippets 95 | ask = f"{green}[{white}?{green}] {yellow}" 96 | success = f"{yellow}[{white}√{yellow}] {green}" 97 | error = f"{blue}[{white}!{blue}] {red}" 98 | info = f"{yellow}[{white}+{yellow}] {cyan}" 99 | info2 = f"{green}[{white}•{green}] {purple}" 100 | 101 | default_dir = getcwd() 102 | default_port = 2121 103 | 104 | banner = f""" 105 | {red}__ ___ _____ _ _____ _____ ____ 106 | {blue}\ \ / (_) ___(_)| ___|_ _| _ \ 107 | {green} \ \ /\ / /| | |_ | || |_ | | | |_) | 108 | {cyan} \ V V / | | _| | || _| | | | __/ 109 | {purple} \_/\_/ |_|_| |_||_| |_| |_| 110 | {blue}{" "*31}[{green}v{cyan}{mm_ver}{blue}] 111 | {cyan}{" "*23}[{blue}By {green}KasRoudra{cyan}]{nc} 112 | """ 113 | 114 | argparser = ArgumentParser() 115 | 116 | argparser.add_argument("-p", "--port", type=int, help=f"WiFiFTP's server port [Default: {default_port}]") 117 | argparser.add_argument("-d", "--directory", help=f"Directory where server will start [Default: {default_dir}]") 118 | argparser.add_argument("-u", "--username", help=f"FTP Username [Default: None]") 119 | argparser.add_argument("-k", "--password", help=f"FTP Password [Default: None]") 120 | argparser.add_argument("-v", "--version", help=f"Prints version of WiFiFTP", action="store_true") 121 | 122 | args = argparser.parse_args() 123 | 124 | arg_port = args.port 125 | arg_directory = args.directory 126 | arg_username = args.username 127 | arg_password = args.password 128 | arg_version = args.version 129 | 130 | 131 | # Check if a package is installed 132 | def is_installed(package): 133 | return shell(f"command -v {package}", True).returncode == 0 134 | 135 | # Print lines slowly 136 | def sprint(text, second=0.05): 137 | for line in text + '\n': 138 | stdout.write(line) 139 | stdout.flush() 140 | sleep(second) 141 | 142 | # Prints colorful texts 143 | def lolcat(text, slow=True, second=0.05): 144 | if is_installed("lolcat"): 145 | run(["lolcat"], input=text, text=True) 146 | else: 147 | if slow: 148 | sprint(text, second) 149 | else: 150 | print(text) 151 | 152 | 153 | def show_banner(): 154 | shell(["clear", "cls"][name == "nt"]) 155 | lolcat(banner, second=0.01) 156 | 157 | 158 | # Run shell commands in python 159 | def shell(command, capture_output=False): 160 | return run(command, shell=True, capture_output=capture_output) 161 | 162 | # Install pip requirements if not found 163 | def inst_deps(): 164 | retry = 3 165 | shell("stty -echoctl") 166 | while retry: 167 | try: 168 | import pyftpdlib 169 | break 170 | except ImportError: 171 | shell(f"{executable} -m pip install pyftpdlib --break-system-packages") 172 | except Exception as e: 173 | print(f"{error}{str(e)}") 174 | if retry == 1: 175 | print(f"{error}Install pyftpdlib manually!{nc}") 176 | exit(1) 177 | retry -= 1 178 | 179 | # Swap bettween ~ and /home/user 180 | def pretty_path(path, rel=True): 181 | new_path = path 182 | home = getenv("HOME") 183 | if path.startswith("\\"): 184 | new_path = path[1:] 185 | if "~" in path: 186 | new_path = new_path.replace("~", home) 187 | if home in path and rel: 188 | new_path = new_path.replace(home, "~") 189 | return new_path 190 | 191 | # Check if a port is being used 192 | def is_available_port(port): 193 | with socket(AF_INET, SOCK_STREAM) as connection: 194 | return connection.connect_ex(("localhost", int(port))) != 0 195 | 196 | # Get user ip address 197 | def get_ip(): 198 | with socket(AF_INET, SOCK_DGRAM) as connection: 199 | connection.settimeout(10) 200 | try: 201 | connection.connect(("8.8.8.8", 80)) 202 | return connection.getsockname()[0] 203 | except error: 204 | print(f"{error}No internet") 205 | exit() 206 | except Exception as e: 207 | print(f"{error}{str(e)}") 208 | exit() 209 | 210 | 211 | # Check if local ip 212 | def check_local(): 213 | ip = get_ip() 214 | if not ip.startswith("192") and not ip.startswith("172"): 215 | print( 216 | f"{error}You are not using any local network!\nPlease connect to a hotspot or router/Wi-Fi!{nc}" 217 | ) 218 | exit() 219 | 220 | def check_args(): 221 | if arg_version: 222 | print(f"{info2}WiFiFTP version: {green}{version}") 223 | exit() 224 | 225 | # Use current directory as ftp path if no path is specified by user 226 | def get_path(): 227 | while True: 228 | if arg_directory is None: 229 | path = pretty_path( 230 | escape( 231 | input( 232 | f"{ask}Enter path (Default: {green}{default_dir}{yellow})\n->{green} " 233 | ) 234 | ), 235 | rel=False 236 | ) 237 | else: 238 | path = arg_directory 239 | if isfile(path): 240 | path = dirname(path) 241 | break 242 | elif isdir(path): 243 | break 244 | elif path == "": 245 | path = default_dir 246 | break 247 | else: 248 | print(f"{error}Invalid path: {path}!{nc}") 249 | print(f"{info}Choosing directory {pretty_path(path)}{nc}") 250 | return path 251 | 252 | # Use 2121 as ftp port if no port is specified by user 253 | def get_port(): 254 | while True: 255 | if arg_port is None: 256 | port = input( 257 | f"{ask}Enter port (Default: {green}{default_port}{yellow})\n->{green} ") 258 | else: 259 | port = str(arg_port) 260 | if port.isdigit() and int(port) > 1023 and int(port) < 65536 and is_available_port(port): 261 | break 262 | elif port == "" and is_available_port(default_port): 263 | port = default_port 264 | break 265 | else: 266 | print(f"{error}Invalid port: {port}!{nc}") 267 | print(f"{info}Choosing port {port}{nc}") 268 | return port 269 | 270 | # https://github.com/giampaolo/pyftpdlib#quick-start 271 | def ftp(path, port): 272 | from pyftpdlib.authorizers import DummyAuthorizer 273 | from pyftpdlib.handlers import FTPHandler 274 | from pyftpdlib.servers import FTPServer 275 | authorizer = DummyAuthorizer() 276 | if arg_username is not None and arg_password is not None: 277 | authorizer.add_user(arg_username, arg_password, path, perm="elradfmw") 278 | else: 279 | authorizer.add_anonymous(path, perm="elradfmw") 280 | 281 | handler = FTPHandler 282 | handler.authorizer = authorizer 283 | 284 | server = FTPServer(("0.0.0.0", port), handler) 285 | server.serve_forever() 286 | 287 | # Print ftp address and start ftp by taking inputs 288 | def start_ftp(): 289 | ip = get_ip() 290 | path = get_path() 291 | port = get_port() 292 | lolcat(f"{info2}Your FTP link is: {green}ftp://{ip}:{port}\n{nc}") 293 | ftp(path, port) 294 | 295 | # StartPoint of script 296 | def main(): 297 | try: 298 | check_args() 299 | inst_deps() 300 | show_banner() 301 | check_local() 302 | start_ftp() 303 | except KeyboardInterrupt: 304 | print(f"\n{info}Closing script....{nc}") 305 | except Exception as e: 306 | print(f"{error}{str(e)}") 307 | 308 | 309 | if __name__ == "__main__": 310 | main() 311 | --------------------------------------------------------------------------------