{"@attributes":{"version":"2.0"},"channel":{"title":"Ceda EI's Blog","link":"https:\/\/cedaei.com\/","description":"Recent content on Ceda EI's Blog","generator":"Hugo -- gohugo.io","language":"en-us","copyright":"CC-BY-SA 4.0","lastBuildDate":"Sat, 25 Dec 2021 00:00:00 +0000","item":[{"title":"Figuring Out HTTPS MITM in India","link":"https:\/\/cedaei.com\/posts\/figuring-out-https-mitm\/","pubDate":"Sat, 25 Dec 2021 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/figuring-out-https-mitm\/","description":"<p>Belonging to India, I am very used to seeing random websites being blocked.\nHowever, today was particularly scary because the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Man-in-the-middle%5c_attack\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>MITM (Man In The Middle\nattack)<\/a>\n happened over\nHTTPS. I visited <a href=\"https:\/\/usebottles.com\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>usebottles.com<\/a>\n over HTTPS and was\nserved with the following page.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/usebottles_censored.webp\" alt=\"\"><\/p>\n<p>Notice the padlock in the address bar. Checking into it, the certificate is\nvalid and signed by <a href=\"https:\/\/cloudflare.com\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Cloudflare<\/a>\n.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/usebottles_certificate.webp\" alt=\"\"><\/p>\n<h2 id=\"further-exploration-and-hypothesis\">Further Exploration and Hypothesis<\/h2>\n<p>The initial hypothesis was that the Indian Government or the ISP has\nCloudflare&rsquo;s signing keys and are serving the blocked page over HTTPS. This\nseems unlikely however and would be a very severe thing and would essentially\nerode all trust in HTTPS at scale as Cloudflare can sign any website&rsquo;s domain\nwhich essentially means that ISPs could MITM<\/p>\n<p>After a bit of exploration, the DNS entry of\n<a href=\"https:\/\/usebottles.com\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>usebottles.com<\/a>\n points to <code>172.67.197.25<\/code>  and\n<code>104.21.92.184<\/code>. I checked that both of these IPs were owned by Cloudflare. To\nensure that the DNS entries weren&rsquo;t being MITM attacked either, I checked for\nthe same from my Hetzner Server.<\/p>\n<p>The second possibility that arises from this is that Cloudflare itself was\nserving the blocked page. While more likely than the previous scenario, it is\nstill unlikely generally. I looked for any notices from Cloudflare about this\nand could not find any.<\/p>\n<p>At this point, I was mostly out of ideas. I looked into the source of the page\nand found something interesting. The entire page&rsquo;s source was the following\n(invalid) HTML:<\/p>\n<pre><code class=\"language-html\">&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1.0,maximum-scale=1.0&quot; \/&gt;\n&lt;style&gt;\n    body {\n        margin: 0px;\n        padding: 0px;\n    }\n\n    iframe {\n        width: 100%;\n        height: 100%\n    }\n&lt;\/style&gt;\n&lt;iframe src=&quot;https:\/\/www.airtel.in\/dot\/&quot; width=&quot;100%&quot; height=&quot;100%&quot; frameborder=0&gt;&lt;\/iframe&gt;\n<\/code><\/pre>\n<p>The most interesting part of this was that the iframe&rsquo;s URL pointed to\n<a href=\"https:\/\/www.airtel.in\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>airtel.in<\/a>\n. Airtel is an ISP in India, however, I was\nnot using internet services from Airtel.<\/p>\n<p>My presumption is based on this.<\/p>\n<h2 id=\"final-hypothesis\">Final Hypothesis<\/h2>\n<p>This is what I presume is happening.<\/p>\n<pre><code>Me &lt;---&gt; Cloudflare &lt;---&gt; usebottles' server\n     1                2\n<\/code><\/pre>\n<p>So far, we have been assuming the MITM is happening at <code>1<\/code> i.e. between Me and\nCloudflare. However, the fact that <code>2<\/code> is secure isn&rsquo;t guaranteed.<\/p>\n<p>My best guess is that the Cloudflare server I am getting connected to happens\nto be using Airtel as the ISP. When Cloudflare&rsquo;s server tries to connect to\nusebottles' server, Cloudflare gets MITM attacked by their ISP - Airtel.\nLikely, SSL is not enforced between Cloudflare and usebottles' server. Thus,\nCloudflare connects to usebottles' server over HTTP.<\/p>\n<p>Normally, a connection would happen the following way:<\/p>\n<ol>\n<li>I connect to <a href=\"https:\/\/usebottles.com\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>https:\/\/usebottles.com\/<\/a>\n<\/li>\n<li>I get connected to Cloudflare&rsquo;s server.<\/li>\n<li>Cloudflare&rsquo;s server reaches out to usebottle&rsquo;s server.<\/li>\n<li>usebottles' server sends a response.<\/li>\n<li>Cloudflare signs the response with the certificate.<\/li>\n<li>I get a webpage over HTTPS.<\/li>\n<\/ol>\n<p>What seems to be happening is:<\/p>\n<ol>\n<li>I connect to <a href=\"https:\/\/usebottles.com\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>https:\/\/usebottles.com\/<\/a>\n<\/li>\n<li>I get connected to Cloudflare&rsquo;s server.<\/li>\n<li>Cloudflare&rsquo;s server reaches out to usebottle&rsquo;s server.<\/li>\n<li><strong>Airtel intercepts the request and sends the blocking page<\/strong><\/li>\n<li>Cloudflare signs the <strong>blocking page<\/strong> with the certificate.<\/li>\n<li>I get <strong>blocking page<\/strong> served over HTTPS.<\/li>\n<\/ol>\n<p>I would be interested in knowing if there are any alternative explanations to\nthis or something I have missed. You can <a href=\"https:\/\/webionite.com\/#contact\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>contact\nme<\/a>\n to let me know!<\/p>\n"},{"title":"Improving Python Dependency Management With pipx and Poetry","link":"https:\/\/cedaei.com\/posts\/python-poetry-pipx\/","pubDate":"Sun, 19 Sep 2021 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/python-poetry-pipx\/","description":"<p>Over time, how I develop applications in python has changed noticeably. I will\ndivide the topic into three sections and see how they tie into each other at\nthe end.<\/p>\n<ul>\n<li>Development<\/li>\n<li>Packaging<\/li>\n<li>Usage<\/li>\n<\/ul>\n<h2 id=\"development\">Development<\/h2>\n<p>Under development, the issues I will focus on are the following:<\/p>\n<ul>\n<li>Dependency Management<\/li>\n<li>Virtualenvs and managing them<\/li>\n<\/ul>\n<p>Historically, the way to do dependency management was through\n<code>requirements.txt<\/code>.  I found <code>requirements.txt<\/code> hard to manage. In that setup,\nadding a dependency and installing it was two steps:<\/p>\n<ul>\n<li>Add the package <code>bar<\/code> to <code>requirements.txt<\/code><\/li>\n<li>Either do <code>pip install bar<\/code> or <code>pip install -r requirements.txt<\/code><\/li>\n<\/ul>\n<p>While focused on development, I would often forget one or both of these steps.\nAlso, the lack of a lock file was a small downside for me (could be a much\nlarger downside for others).  The separation between <code>pip<\/code> and\n<code>requirements.txt<\/code> can also easily lead you to accidentally depend on packages\ninstalled on your system or in your virtualenv but not specified in your\n<code>requirements.txt<\/code>.<\/p>\n<p>Managing virtualenvs was also difficult. As a virtualenv and a project are not\nrelated, you need a directory structure. Otherwise, you can&rsquo;t tell which\nvirtualenv is being used for which project. You can use the same virtualenvs\nfor multiple projects, but that partially defeats the point of virtualenvs and\nmakes <code>requirements.txt<\/code> more error-prone (higher chances of forgetting to add\npackages to it). The approach generally used is one of the following two:<\/p>\n<pre><code>foo\/\n\u251c\u2500\u2500 foo_src\/\n\u2514\u2500\u2500 foo_venv\/\n<\/code><\/pre>\n<p>or<\/p>\n<pre><code>foo_src\/\n\u2514\u2500\u2500 venv\/\n<\/code><\/pre>\n<p>I preferred the second one as the first one nests the source code one\ndirectory deeper.<\/p>\n<h3 id=\"a-new-standard---pyprojecttoml\">A new standard - <code>pyproject.toml<\/code><\/h3>\n<p>In <a href=\"https:\/\/www.python.org\/dev\/peps\/pep-0518\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>PEP-518<\/a>\n, python standardized\nthe <code>pyproject.toml<\/code> file which allows users to choose alternate build systems\nfor package generation.<\/p>\n<p>One such project that provides an alternate build system is\n<a href=\"https:\/\/python-poetry.org\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Poetry<\/a>\n. Poetry hits the nail on the head and\nsolves my major gripes with traditional tooling.<\/p>\n<h3 id=\"poetry-and-virtualenvs\">Poetry and virtualenvs<\/h3>\n<p>Poetry manages the virtualenvs automatically and keeps track of which project\nuses which virtualenv automatically. Working on an existing project which uses\npoetry is as simple as this:<\/p>\n<pre><code class=\"language-bash\">$ git clone https:\/\/gitlab.com\/ceda_ei\/verlauf\n$ poetry install\n<\/code><\/pre>\n<p>The <code>poetry install<\/code> command sets up the virtualenv, install all the required\ndependencies inside that, and sets up any commands accordingly (I will get to\nthis soon).  To activate the virtualenv, simply run:<\/p>\n<pre><code class=\"language-bash\">. &quot;$(poetry env info --path)\/bin\/activate&quot;\n<\/code><\/pre>\n<p>I wrap this in a small function which lets me toggle it quickly:<\/p>\n<pre><code class=\"language-bash\">function poet() {\n\tPOET_MANUAL=1\n\tif [[ -v VIRTUAL_ENV ]]; then\n\t\tdeactivate\n\telse\n\t\t. &quot;$(poetry env info --path)\/bin\/activate&quot;\n\tfi\n}\n<\/code><\/pre>\n<p>Running <code>poet<\/code> activates the virtualenv if it is not active and deactivates it if\nit is active. To make things even easier, I automatically activate and\ndeactivate the virtualenv as I enter and leave the project directory.  To do\nso, simply drop this in your <code>.bashrc<\/code>.<\/p>\n<pre><code class=\"language-bash\">function find_in_parent() {\n\tlocal path\n\tIFS=&quot;\/&quot; read -ra path &lt;&lt;&lt;&quot;$PWD&quot;\n\tfor ((i=${#path[@]}; i &gt; 0; i--)); do\n\t\tlocal current_path=&quot;&quot;\n\t\tfor ((j=1; j&lt;i; j++)); do\n\t\t\tcurrent_path=&quot;$current_path\/${path[j]}&quot;\n\t\tdone\n\t\tif [[ -e &quot;${current_path}\/$1&quot; ]]; then\n\t\t\techo &quot;${current_path}\/&quot;\n\t\t\treturn\n\t\tfi\n\tdone\n\treturn 1\n}\n\nfunction auto_poet() {\n\tret=&quot;$?&quot;\n\tif [[ -v POET_MANUAL ]]; then\n\t\treturn $ret\n\tfi\n\tif find_in_parent pyproject.toml &amp;&gt; \/dev\/null; then\n\t\tif [[ ! -v VIRTUAL_ENV ]]; then\n\t\t    if BASE=&quot;$(poetry env info --path)&quot;; then\n\t\t\t. &quot;$BASE\/bin\/activate&quot;\n\t\t\tPS1=&quot;&quot;\n\t\t    else\n\t\t\tPOET_MANUAL=1\n\t\t    fi\n\t\tfi\n\telif [[ -v VIRTUAL_ENV ]]; then\n\t\tdeactivate\n\tfi\n\treturn $ret\n}\n\nPROMPT_COMMAND=&quot;auto_poet;$PROMPT_COMMAND&quot;\n<\/code><\/pre>\n<p>This ties in well with the <code>poet<\/code> function; if you use <code>poet<\/code> anytime in a bash\nsession, activation switches from automatic to manual and changing directories\nno longer auto-toggles the virtualenv.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/auto_poet.webp\" alt=\"auto_poet and poet in action\"><\/p>\n<h3 id=\"poetry-and-dependency-management\">Poetry and dependency management<\/h3>\n<p>Instead of using <code>requirements.txt<\/code>, poetry stores the dependencies inside\n<code>pyproject.toml<\/code>.  Poetry is more strict compared to <code>pip<\/code> in resolving\nversioning issues.  Dependencies and dev-dependencies are stored inside\n<code>tool.poetry.dependencies<\/code> and <code>tool.poetry.dev-dependencies<\/code> respectively.\nHere is an example of a <code>pyproject.toml<\/code> for a project I am working on.<\/p>\n<pre><code class=\"language-toml\">[tool.poetry]\nname = &quot;bells&quot;\nversion = &quot;0.3.0&quot;\ndescription = &quot;Bells is a program for keeping track of sound recordings.&quot;\nauthors = [&quot;Ceda EI &lt;ceda_ei@webionite.com&gt;&quot;]\nlicense = &quot;GPL-3.0&quot;\nreadme = &quot;README.md&quot;\nhomepage = &quot;https:\/\/gitlab.com\/ceda_ei\/bells.git&quot;\nrepository = &quot;https:\/\/gitlab.com\/ceda_ei\/bells.git&quot;\n\n[tool.poetry.dependencies]\npython = &quot;&gt;=3.7,&lt;3.11&quot;\nclick = &quot;^8.0.1&quot;\nquestionary = &quot;^1.10.0&quot;\nsounddevice = &quot;^0.4.2&quot;\nSoundFile = &quot;^0.10.3&quot;\nnumpy = &quot;^1.21.2&quot;\n\n[tool.poetry.dev-dependencies]\n\n[build-system]\nrequires = [&quot;poetry-core&gt;=1.0.0&quot;]\nbuild-backend = &quot;poetry.core.masonry.api&quot;\n\n# I will talk about this section soon\n[tool.poetry.scripts]\nbells = &quot;bells.__main__:main&quot;\n<\/code><\/pre>\n<p>One of the upsides of poetry is that you don&rsquo;t have to manage the dependencies\nin <code>pyproject.toml<\/code> file yourself. Poetry adds an <code>npm<\/code>-like interface for\nadding and removing dependencies.  To add a dependency to your project, simply\nrun <code>poetry add bar<\/code> and it will add it to your <code>pyproject.toml<\/code> file and\ninstall it in the virtualenv as well. To remove a dependency, just run <code>poetry remove bar<\/code>. For development dependencies, just add the <code>--dev<\/code> flag to the\ncommands.<\/p>\n<h2 id=\"packaging\">Packaging<\/h2>\n<p>Since poetry replaces the build system, we can now configure the build using\npoetry via <code>pyproject.toml<\/code>. Inside <code>pyproject.toml<\/code>, the <code>tool.poetry<\/code> section\nstores all the build info needed; <code>tool.poetry<\/code> contains the metadata,\n<code>tool.poetry.dependencies<\/code> contains the dependencies, <code>tool.poetry.source<\/code>\ncontains private repository details (in case, you don&rsquo;t want to use PyPi).<\/p>\n<p>One of the options is <code>tool.poetry.scripts<\/code>. It contains scripts that the\nproject exposes. This replaces <code>console_scripts<\/code> in <code>entry_points<\/code> of\n<code>setuptools<\/code>.<\/p>\n<p>For example,<\/p>\n<pre><code class=\"language-toml\">[tool.poetry.scripts]\nfoobar = &quot;foo.bar:main&quot;\n<\/code><\/pre>\n<p>This will add a script named <code>foobar<\/code> in your <code>PATH<\/code>. Running that is\nequivalent to running the following script<\/p>\n<pre><code class=\"language-python\">from foo.bar import main\n\nif __name__ == &quot;__main__&quot;:\n    main()\n<\/code><\/pre>\n<p>For further details, check the\n<a href=\"https:\/\/python-poetry.org\/docs\/pyproject\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>reference<\/a>\n.<\/p>\n<p>Poetry also removes the need for manually doing editable installs (<code>pip install -e .<\/code>).  The package is automatically installed as editable when you run\n<code>poetry install<\/code>. Any scripts specified in <code>tool.poetry.scripts<\/code> are\nautomatically available in your <code>PATH<\/code> when you activate the <code>venv<\/code>.<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1<\/a><\/sup><\/p>\n<p>To build the package, simply run <code>poetry build<\/code>. This will generate a wheel and\na tarball in the dist folder.<\/p>\n<p>To publish the package to PyPi (or another repo), simply run <code>poetry publish<\/code>.\nYou can combine the build and publish into one command with <code>poetry publish --build<\/code>.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/poetry_build.webp\" alt=\"example of poetry build\"><\/p>\n<h2 id=\"usage\">Usage<\/h2>\n<p>This part is more user-facing rather than dev-facing. If you want to use two\npackages globally that expose some scripts to the user, (e.g. <code>awscli<\/code>,\n<code>youtube-dl<\/code>, etc.) the general approach to do so is to run something like <code>pip install --user youtube-dl<\/code>. This install the package at the user level and\nexposes the script through <code>~\/.local\/bin\/youtube-dl<\/code>. However, this installs\nall the packages at the same user level. Hypothetically, if you have two\npackages <code>foo<\/code> and <code>bar<\/code> which have conflicting dependencies, this causes an\nissue. If you run,<\/p>\n<pre><code class=\"language-bash\">$ pip install foo\n$ pip install bar\n$ bar # works\n$ foo # breaks because of dependency mismatch\n<\/code><\/pre>\n<p>While installing <code>bar<\/code>, <code>pip<\/code> will install the dependencies for <code>bar<\/code> which\nwill break <code>foo<\/code> after warning you<sup id=\"fnref:2\"><a href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\">2<\/a><\/sup>.<\/p>\n<p>To solve this, there is <a href=\"https:\/\/github.com\/pypa\/pipx\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n><code>pipx<\/code><\/a>\n. Pipx installs\neach package in a separate virtualenv without requiring the user to activate\nsaid virtualenv before using the package.<sup id=\"fnref:3\"><a href=\"#fn:3\" class=\"footnote-ref\" role=\"doc-noteref\">3<\/a><\/sup><\/p>\n<p>In the same scenario as before, doing the following works just fine.<\/p>\n<pre><code class=\"language-bash\">$ pipx install foo\n$ pipx install bar\n$ bar # works\n$ foo # also works\n<\/code><\/pre>\n<p>In this scenario, both <code>bar<\/code> and <code>foo<\/code> are installed in separate virtualenvs so\nthe dependency conflict doesn&rsquo;t matter.<\/p>\n<h2 id=\"some-more-things-from-my-bashrc\">Some more things from my bashrc<\/h2>\n<pre><code class=\"language-bash\">\nfunction wrapper_no_poet() {\n\tlocal last_env\n\tif [[ -v VIRTUAL_ENV ]]; then\n\t\tlast_env=&quot;$VIRTUAL_ENV&quot;\n\t\tdeactivate\n\tfi\n\t&quot;$@&quot;\n\tret=$?\n\tif [[ -v last_env ]]; then\n\t\t. &quot;$last_env\/bin\/activate&quot;\n\tfi\n\treturn $ret\n}\n\nalias wnp='wrapper_no_poet'\nalias pm='POET_MANUAL=1'\n<\/code><\/pre>\n<p>Prefixing any command with <code>wnp<\/code> runs it outside the virtualenv if a virtualenv\nis active. Running <code>pm<\/code> turns off automatic virtualenv activation.<\/p>\n<section class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn:1\" role=\"doc-endnote\">\n<p>This also allows for a nice switch between the development and production\nversions of the app. Essentially, when the virtualenv is active, you are\nusing the development script while when it is deactivated, you are using\nthe global (likely production) version.&#160;<a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<li id=\"fn:2\" role=\"doc-endnote\">\n<p>To be precise, it will warn you that it broke <code>foo<\/code> but will still\ncontinue with the installation&#160;<a href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<li id=\"fn:3\" role=\"doc-endnote\">\n<p>For development, poetry also provides <code>poetry run<\/code> which runs a file\nwithout having to activate the virtualenv.&#160;<a href=\"#fnref:3\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<\/ol>\n<\/section>\n"},{"title":"Ideas from my Development Setup: Clipboard","link":"https:\/\/cedaei.com\/posts\/ideas-from-my-dev-setup-clipboard\/","pubDate":"Wed, 07 Oct 2020 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/ideas-from-my-dev-setup-clipboard\/","description":"<h2 id=\"ideas\">Ideas<\/h2>\n<p>The clipboard is a basic yet integral part of every environment. Over time, I\nhave had quite a few ideas regarding clipboards. I have implemented the\nfollowing ideas currently:<\/p>\n<h3 id=\"clipboard-swap\">Clipboard Swap<\/h3>\n<p>Inspired by vim&rsquo;s registers, I thought about implementing something similar on\na system level for Xorg. My current implementation basically swaps the contents\nof the clipboard with another clipboard. Currently, the workflow looks\nsomething like this.<\/p>\n<ul>\n<li>Copy text <code>abc<\/code>: Writes <code>abc<\/code> to the main clipboard.<\/li>\n<li>Press <code>Ctrl+`<\/code>: Swaps content of hidden clipboard with main clipboard.<\/li>\n<li>Copy text <code>def<\/code>: Writes <code>def<\/code> to the main clipboard.<\/li>\n<li>Paste with <code>Ctrl+v<\/code>: Pastes text <code>def<\/code> as expected.<\/li>\n<li>Press <code>Ctrl+`<\/code>: Swaps content of hidden clipboard with main clipboard.<\/li>\n<li>Paste: Pastes text <code>abc<\/code>.<\/li>\n<\/ul>\n<h3 id=\"implicit-copying\">Implicit Copying<\/h3>\n<p>Some time ago, a realization hit me that I practically always select text for\nthe sake of copying or cutting it. Cutting is just copying and then deleting\nthat text. So everytime I select text, it is for copying it. Due to this, I\nimplemented implicit copying where text is copied just by selecting it.  So,\nthe workflow for copying is reduced from select, ctrl+c, paste to select, paste\nwhile the workflow for cutting is same.<\/p>\n<h2 id=\"clipboards-in-xorg\">Clipboards in Xorg<\/h2>\n<p>In Xorg, clipboards are called selections. Xorg has three selections:\nPrimary, Secondary and Clipboard.<\/p>\n<ul>\n<li>Primary: Whenever text is selected, it is copied into primary selection. You\ncan paste contents of the primary selection by pressing middle mouse button\nin most applications.<\/li>\n<li>Secondary: There is no consensus on what this is supposed to be used for and\nhence it is used by practically nothing.<\/li>\n<li>Clipboard: This is the selection that practically every application interacts\nwith. Ctrl-C and Ctrl-V are commonly bound to writing to this clipboard.<\/li>\n<\/ul>\n<h2 id=\"implementation\">Implementation<\/h2>\n<p>Secondary selection is the perfect selection to swap the contents of Clipboard\nselection with. It is practically never used so nothing will overwrite the\ncontents of secondary selection.<\/p>\n<p>Here is the <code>clip_swap.sh<\/code> which I have bound to <code>Ctrl+`<\/code> in my i3\nconfig.<\/p>\n<pre><code class=\"language-bash\">#!\/usr\/bin\/env bash\n\nx=$(xclip -selection secondary -out)\nxclip -selection clipboard -out | xclip -selection secondary\necho &quot;$x&quot; | xclip -selection clipboard\n<\/code><\/pre>\n<p>For implementing implicit copying, I basically needed a daemon that watched for\nchanges in primary selection and copies them over to clipboard selection. Here\nare the contents of <code>clip_syncd.sh<\/code><\/p>\n<pre><code class=\"language-bash\">#!\/usr\/bin\/env bash\n# Daemon to synchronize PRIMARY selection with CLIPBOARD selection\n\nold=&quot;&quot;\nwhile :; do\n\tnew=$(xclip -selection primary -out) || { sleep 0.5; continue; }\n\tif [[ $new != &quot;$old&quot; ]]; then\n\t\txclip -selection primary -out | xclip -selection clipboard -in\n\t\told=&quot;$new&quot;\n\tfi\n\tsleep 0.5\ndone\n<\/code><\/pre>\n"},{"title":"Ideas from my Development Setup: Always Tmux","link":"https:\/\/cedaei.com\/posts\/ideas-from-my-dev-setup-always-tmux\/","pubDate":"Wed, 30 Sep 2020 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/ideas-from-my-dev-setup-always-tmux\/","description":"<p>Back when I first learnt tmux, I realized it was a really valuable tool. Soon\nafterwards, I found myself in need of wanting to make a split only to find out\nI wasn&rsquo;t in tmux. This would lead to:<\/p>\n<ul>\n<li>Killing the running process.<\/li>\n<li>Starting tmux.<\/li>\n<li>Restarting the previous process.<\/li>\n<\/ul>\n<p>In pursuit of an ideal solution, I added a tiny script in my scripts directory\nwhich was called by my bashrc.<\/p>\n<h2 id=\"current-workflow\">Current Workflow<\/h2>\n<p>My current workflow simply starts by opening the terminal. Instead of the bash\nprompt, I am greeted by this.<\/p>\n<pre><code>Choose the terminal to attach:\n1 - 12: 3 windows (created Wed Sep 30 14:26:37 2020) (attached)\n2 - tana: 3 windows (created Wed Sep 30 18:17:24 2020) (attached)\n3 - userbot: 1 windows (created Tue Sep 29 18:37:19 2020)\n4 - ytc: 1 windows (created Tue Sep 29 18:37:19 2020)\n\nCreate a new session by entering a name for it\n<\/code><\/pre>\n<p>At this point, I either<\/p>\n<ul>\n<li>enter a number (in this case from 1 to 4) to connect to an existing session.<\/li>\n<li>enter a name to create a named tmux session.<\/li>\n<li>hit enter (or C-D) to create an unnamed session (tmux will name it\nsequentially).<\/li>\n<li>type nil and hit enter to drop to shell without starting tmux<\/li>\n<\/ul>\n<h2 id=\"implementation\">Implementation<\/h2>\n<p>In my <code>.bashrc<\/code>, live these lines.<\/p>\n<pre><code class=\"language-bash\">if [[ ! -v TMUX &amp;&amp; $TERM_PROGRAM != &quot;vscode&quot; ]]; then\n\ttmux_chooser &amp;&amp; exit\nfi\n<\/code><\/pre>\n<p>Although I use vim as my sole editor, I needed to demo something in VSCode and\nfor that case I have added an exception so that the script does not run the\n<code>tmux_chooser<\/code> in VSCode&rsquo;s integrated terminal.<\/p>\n<p>Here is the source of <code>tmux_chooser<\/code> called above.<\/p>\n<pre><code class=\"language-bash\">#!\/usr\/bin\/bash\n# shellcheck disable=SC2207\n\n# Doesn't let you press Ctrl-C\nfunction ctrl_c() {\n\techo -e &quot;\\renter nil to drop to normal prompt&quot;\n}\n\ntrap ctrl_c SIGINT\n\nno_of_terminals=$(tmux list-sessions | wc -l)\nIFS=$'\\n'\noutput=($(tmux list-sessions))\noutput_names=($(tmux list-sessions -F\\#S))\nk=1\necho &quot;Choose the terminal to attach: &quot;\nfor i in &quot;${output[@]}&quot;; do\n\techo &quot;$k - $i&quot;\n\t((k++))\ndone\necho\necho &quot;Create a new session by entering a name for it&quot;\nread -r input\nif [[ $input == &quot;&quot; ]]; then\n\ttmux new-session\nelif [[ $input == 'nil' ]]; then\n\texit 1\nelif [[ $input =~ ^[0-9]+$ ]] &amp;&amp; [[ $input -le $no_of_terminals ]]; then\n\tterminal_name=&quot;${output_names[input - 1]}&quot;\n\ttmux attach -t &quot;$terminal_name&quot;\nelse\n\ttmux new-session -s &quot;$input&quot;\nfi\nexit 0\n<\/code><\/pre>\n"},{"title":"Vim-like Layer for Xorg and Wayland","link":"https:\/\/cedaei.com\/posts\/vim-like-layer-for-xorg-wayland\/","pubDate":"Tue, 25 Aug 2020 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/vim-like-layer-for-xorg-wayland\/","description":"<h1 id=\"the-goal\">The Goal<\/h1>\n<h2 id=\"insert-mode\">Insert Mode<\/h2>\n<p><img src=\"https:\/\/cedaei.com\/images\/insert_mode.jpg\" alt=\"Insert Mode: A keyboard layout similar to normal QWERTYlayout\"><\/p>\n<h2 id=\"normal-mode\">Normal Mode<\/h2>\n<p><img src=\"https:\/\/cedaei.com\/images\/normal_mode.jpg\" alt=\"Normal Mode: A keyboard layout with the alphabet keys replaced with shortcutkey\"><\/p>\n<p>Inspired by vim, I wanted to create a layer on top of my keyboard which worked\nlike a shortcut layer. So, to start off, I found out about XKB<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1<\/a><\/sup>. XKB is the\nXorg Keyboard Extension which tells Xorg on how to react to input from\nkeyboard.  After reading through some source code, I found out that Xorg has\nsupport for function keys F1 - F35<sup id=\"fnref:2\"><a href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\">2<\/a><\/sup>. The general idea here was:<\/p>\n<ul>\n<li>Create an insert mode layout for text input.<\/li>\n<li>Replace keys with relevant keys in normal mode (e.g. replace j with Down) and\nfor keys that require executing a command, replace then with a function key\nabove F12 (e.g. replace q with F13).<\/li>\n<li>Bind all the function keys above F12 to the respective functions.<\/li>\n<\/ul>\n<p>To start off, I began a fresh Xorg session with nothing modifying the keys\n(removed <code>xmodmap<\/code> from startup) and first dumped the current layout into a\nfile.<\/p>\n<pre><code class=\"language-bash\">xkbcomp $DISPLAY ~\/.xkb\/insert.xkb\n<\/code><\/pre>\n<p>This was my starting point. I made changes to this file which were common to\nboth Insert and Normal mode. e.g. replaced <code>Caps Lock<\/code> with <code>Ctrl<\/code> and made\n<code>Shift+Caps Lock<\/code> <code>Caps Lock<\/code>. Also, I unbound <code>Alt_R<\/code> as a modifier so that I\ncould use that as a switch between Normal and Insert Mode.<\/p>\n<p>Here is a diff between the original layout and Insert mode.<\/p>\n<pre><code>1323c1321\n&lt;     key &lt;CAPS&gt; {         [       Caps_Lock ] };\n---\n&gt;     key &lt;CAPS&gt; {         [       Control_L,       Caps_Lock ] };\n1551c1549\n&lt;     modifier_map Lock { &lt;CAPS&gt; };\n---\n&gt;     modifier_map Control { &lt;CAPS&gt; };\n1555d1552\n&lt;     modifier_map Mod1 { &lt;RALT&gt; };\n<\/code><\/pre>\n<p>Next, I copied <code>~\/.xkb\/insert.xkb<\/code> to <code>~\/.xkb\/normal.xkb<\/code>. I replaced keys as\nper the plan.<\/p>\n<p>Here is a diff between Insert mode and Normal mode.<\/p>\n<pre><code class=\"language-diff\">1200c1200\n&lt;         symbols[Group1]= [               q,               Q ]\n---\n&gt;         symbols[Group1]= [F13]\n1204c1204\n&lt;         symbols[Group1]= [               w,               W ]\n---\n&gt;         symbols[Group1]= [F14]\n1208c1208\n&lt;         symbols[Group1]= [               e,               E ]\n---\n&gt;         symbols[Group1]= [F15]\n1212c1212\n&lt;         symbols[Group1]= [               r,               R ]\n---\n&gt;         symbols[Group1]= [F16]\n1216c1216\n&lt;         symbols[Group1]= [               t,               T ]\n---\n&gt;         symbols[Group1]= [F17]\n1220c1220\n&lt;         symbols[Group1]= [               y,               Y ]\n---\n&gt;         symbols[Group1]= [F18]\n1224c1224\n&lt;         symbols[Group1]= [               u,               U ]\n---\n&gt;         symbols[Group1]= [F19]\n1228c1228\n&lt;         symbols[Group1]= [               i,               I ]\n---\n&gt;         symbols[Group1]= [Alt_R]\n1232c1232\n&lt;         symbols[Group1]= [               o,               O ]\n---\n&gt;         symbols[Group1]= [F20]\n1236c1236\n&lt;         symbols[Group1]= [               p,               P ]\n---\n&gt;         symbols[Group1]= [F21]\n1244c1244\n&lt;         symbols[Group1]= [               a,               A ]\n---\n&gt;         symbols[Group1]= [F22]\n1248c1248\n&lt;         symbols[Group1]= [               s,               S ]\n---\n&gt;         symbols[Group1]= [Delete]\n1252c1252\n&lt;         symbols[Group1]= [               d,               D ]\n---\n&gt;         symbols[Group1]= [BackSpace]\n1256c1256\n&lt;         symbols[Group1]= [               f,               F ]\n---\n&gt;         symbols[Group1]= [Home]\n1260c1260\n&lt;         symbols[Group1]= [               g,               G ]\n---\n&gt;         symbols[Group1]= [End]\n1264c1264\n&lt;         symbols[Group1]= [               h,               H ]\n---\n&gt;         symbols[Group1]= [Left]\n1268c1268\n&lt;         symbols[Group1]= [               j,               J ]\n---\n&gt;         symbols[Group1]= [Down]\n1272c1272\n&lt;         symbols[Group1]= [               k,               K ]\n---\n&gt;         symbols[Group1]= [Up]\n1276c1276\n&lt;         symbols[Group1]= [               l,               L ]\n---\n&gt;         symbols[Group1]= [Right]\n1285c1285\n&lt;         symbols[Group1]= [               z,               Z ]\n---\n&gt;         symbols[Group1]= [F23]\n1289c1289\n&lt;         symbols[Group1]= [               x,               X ]\n---\n&gt;         symbols[Group1]= [F24]\n1293c1293\n&lt;         symbols[Group1]= [               c,               C ]\n---\n&gt;         symbols[Group1]= [F25]\n1297c1297\n&lt;         symbols[Group1]= [               v,               V ]\n---\n&gt;         symbols[Group1]= [F26]\n1301c1301\n&lt;         symbols[Group1]= [               b,               B ]\n---\n&gt;         symbols[Group1]= [F27]\n1305c1305\n&lt;         symbols[Group1]= [               n,               N ]\n---\n&gt;         symbols[Group1]= [Next]\n1309c1309\n&lt;         symbols[Group1]= [               m,               M ]\n---\n&gt;         symbols[Group1]= [Prior]\n<\/code><\/pre>\n<p>At this point, <code>normal.xkb<\/code> file defines the following layout.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/normal_mode_unbound.jpg\" alt=\"Normal Mode: A keyboard \"><\/p>\n<p>Now, we need a script that switches between layouts. To load an layout in Xorg, we use<\/p>\n<pre><code class=\"language-bash\">xkbcomp ~\/.xkb\/normal.xkb &quot;$DISPLAY&quot;\n<\/code><\/pre>\n<p>Sway supports this via the input command in the following form.<\/p>\n<pre><code class=\"language-bash\">swaymsg input '*' xkb_file ~\/.xkb\/normal.xkb\n<\/code><\/pre>\n<p>The following script cycles through the layouts when it is called. It also\nallows to add more layouts later (just add them to layouts array and it will\ncycle in the order of the array).<\/p>\n<pre><code class=\"language-bash\">#!\/usr\/bin\/env bash\n# Usage: xkb_swapper.sh [layout_name]\n\nfunction set_layout() {\n\techo &quot;Setting layout to $1&quot;\n\tif [[ -v WAYLAND_DISPLAY ]]; then\n\t\tswaymsg input '*' xkb_file ~\/.xkb\/&quot;$1&quot;.xkb\n\telse\n\t\txkbcomp ~\/.xkb\/&quot;$1&quot;.xkb &quot;$DISPLAY&quot;\n\tfi\n\techo &quot;$1&quot; &gt; ~\/.cache\/xkb-curr-&quot;$DISPLAY&quot;\n}\nlayouts=(insert normal)\ncurrent_layout=$(cat ~\/.cache\/xkb-curr-&quot;$DISPLAY&quot; || echo &quot;&quot;)\n\nif [[ $1 != &quot;&quot; ]]; then\n\tset_layout &quot;$1&quot;\n\texit\nfi\nif [[ $current_layout == &quot;&quot; ]]; then\n\techo &quot;No current layout found!&quot;\n\tset_layout &quot;${layouts[0]}&quot;\nfi\n\ni=0\nwhile [[ $i -lt ${#layouts[@]} ]]; do\n\tif [[ $current_layout == &quot;${layouts[$i]}&quot; ]]; then\n\t\tnew_idx=&quot;$((i+1))&quot;\n\t\tif [[ $new_idx -eq ${#layouts[@]} ]]; then\n\t\t\tset_layout &quot;${layouts[0]}&quot;\n\t\telse\n\t\t\tset_layout &quot;${layouts[$new_idx]}&quot;\n\t\tfi\n\t\texit\n\tfi\n\t((i++))\ndone\n\necho &quot;Current Layout doesn't exist!&quot;\nset_layout &quot;${layouts[0]}&quot;\n<\/code><\/pre>\n<p>The above script works with all Xorg based DE\/WMs as well as Sway (wayland\ncompositor). I saved it as <code>xkb_swapper.sh<\/code> in my <code>PATH<\/code>. Calling the script\nwithout any argument cycles through the layouts. If arguments are passed, the\nfirst argument is taken as layout name and layout is changed to that.<\/p>\n<p>The last step is binding the function keys and <code>Alt_R<\/code> to commands to execute.\nHere are some of the parts of my i3 config that bind the function keys.<\/p>\n<pre><code>bindsym Alt_R exec xkb_swapper.sh\nbindsym 0xffca kill\nbindsym 0xffcf exec volchange -5\nbindsym 0xffd0 exec volchange +5\nbindsym 0xffd1 exec brightness -200\nbindsym 0xffd2 exec brightness +200\nbindsym 0xffcb exec mpc prev\nbindsym 0xffcc exec mpc toggle\nbindsym 0xffcd exec mpc next\n<\/code><\/pre>\n<p><code>i3<\/code> doesn&rsquo;t seem to accept <code>F13<\/code> - <code>F35<\/code> as keynames however it accepts the\nkeycodes<sup id=\"fnref:2\"><a href=\"#fn:2\" class=\"footnote-ref\" role=\"doc-noteref\">2<\/a><\/sup>.  Here is a small list for easy access.<\/p>\n<pre><code>0xffbe   F1\n0xffbf   F2\n0xffc0   F3\n0xffc1   F4\n0xffc2   F5\n0xffc3   F6\n0xffc4   F7\n0xffc5   F8\n0xffc6   F9\n0xffc7   F10\n0xffc8   F11\n0xffc9   F12\n0xffca   F13\n0xffcb   F14\n0xffcc   F15\n0xffcd   F16\n0xffce   F17\n0xffcf   F18\n0xffd0   F19\n0xffd1   F20\n0xffd2   F21\n0xffd3   F22\n0xffd4   F23\n0xffd5   F24\n0xffd6   F25\n0xffd7   F26\n0xffd8   F27\n0xffd9   F28\n0xffda   F29\n0xffdb   F30\n0xffdc   F31\n0xffdd   F32\n0xffde   F33\n0xffdf   F34\n0xffe0   F35\n<\/code><\/pre>\n<h1 id=\"bonus-displaying-the-current-mode-in-your-bar\">Bonus: Displaying the current mode in your bar<\/h1>\n<p>The script stores the mode in <code>~\/.cache\/xkb-curr-$DISPLAY<\/code>. <code>cat<\/code> that and\nwrap in your bar&rsquo;s config. Here is my config for\n<a href=\"https:\/\/github.com\/greshake\/i3status-rust\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>i3status-rust<\/a>\n.<\/p>\n<pre><code class=\"language-toml\">[[block]]\nblock = &quot;custom&quot;\ncommand = &quot;echo -en '\\\\uf11c '; cat ~\/.cache\/xkb-curr-$DISPLAY&quot;\ninterval = 0.5\n<\/code><\/pre>\n<section class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn:1\" role=\"doc-endnote\">\n<p>As always, the <a href=\"https:\/\/wiki.archlinux.org\/index.php\/X_keyboard_extension\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Arch Wiki page on XKB<\/a>\n is a nice place to start.&#160;<a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<li id=\"fn:2\" role=\"doc-endnote\">\n<p>You can find all the defined keys in <code>\/usr\/include\/X11\/keysymdef.h<\/code>.&#160;<a href=\"#fnref:2\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<\/ol>\n<\/section>\n"},{"title":"Vim: Expressions in replacement text","link":"https:\/\/cedaei.com\/posts\/vim-sub-replace-expression\/","pubDate":"Sun, 14 Jun 2020 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/vim-sub-replace-expression\/","description":"<p>Often times I find myself needing to modify the selection in a substitute command in a non-trivial way. From a recent example, I needed to convert the numbers in the <code>id<\/code> of the <code>div<\/code>s to hexadecimal after decrementing them by 1.<\/p>\n<p>Basically, I had to convert<\/p>\n<pre><code class=\"language-html\">&lt;div id=&quot;someprefix1&quot;&gt;\n\t ...\n&lt;\/div&gt;\n&lt;div id=&quot;someprefix2&quot;&gt;\n\t...\n&lt;\/div&gt;\n&lt;div id=&quot;someprefix3&quot;&gt;\n\t...\n&lt;\/div&gt;\n&lt;div id=&quot;someprefix4&quot;&gt;\n\t...\n&lt;\/div&gt;\n...\n&lt;div id=&quot;someprefix80&quot;&gt;\n\t...\n&lt;\/div&gt;\n<\/code><\/pre>\n<p>to<\/p>\n<pre><code class=\"language-html\">&lt;div id=&quot;someprefix0&quot;&gt;\n\t ...\n&lt;\/div&gt;\n&lt;div id=&quot;someprefix1&quot;&gt;\n\t...\n&lt;\/div&gt;\n&lt;div id=&quot;someprefix2&quot;&gt;\n\t...\n&lt;\/div&gt;\n&lt;div id=&quot;someprefix3&quot;&gt;\n\t...\n&lt;\/div&gt;\n...\n&lt;div id=&quot;someprefix4F&quot;&gt;\n\t...\n&lt;\/div&gt;\n<\/code><\/pre>\n<p>Doing this manually is tiring, I could have used macros but I instead chose an even simpler approach.<\/p>\n<p>Vim allows to use expressions in the replacement part of the substitute command if it starts with <code>\\=<\/code>. So, the replacement can be simply done using <code>\\=<\/code> and <code>printf<\/code>. What I did was simply use the following command after selecting the text in a visual selection.<\/p>\n<p><code>:'&lt;,'&gt;s\/\\v(someprefix)(\\d+)\/\\=printf(&quot;%s%X&quot;, submatch(1), submatch(2) - 1)<\/code><\/p>\n<p>The breakdown is as follows:<\/p>\n<ul>\n<li><code>:'&lt;,'&gt;<\/code>: Sets the range to visual selection.<\/li>\n<li><code>s\/<\/code>: Starts the substitution.<\/li>\n<li><code>\\v<\/code>: Enables &ldquo;very magic mode&rdquo;. This allows for writing simpler regular expressions without having to escape special characters.<\/li>\n<li><code>(someprefix)(\\d+)<\/code>: Captures the string <code>someprefix<\/code> in group 1 and the number that follows in group 2.<\/li>\n<li><code>\/<\/code>: Ends the search and starts replacement.<\/li>\n<li><code>\\=<\/code>: Tells Vim to treat the following text as an expression.<\/li>\n<li><code>printf(<\/code>: <code>printf<\/code> is a function that takes a format string and values for the format parameters.<\/li>\n<li><code>&quot;%s%X&quot;<\/code>: The format string. <code>%s<\/code> means to print the parameter as a string. <code>%X<\/code> means to convert the parameter to a hexadecimal number in capital letters (<code>%x<\/code> for small).<\/li>\n<li><code>submatch(1)<\/code>: It retrieves the first matched group.<\/li>\n<li><code>submatch(2) - 1<\/code>: It retrieves the second matched group and subtracts one from it.<\/li>\n<\/ul>\n<h3 id=\"references\">References<\/h3>\n<ul>\n<li><code>:help sub-replace-expression<\/code><\/li>\n<li><code>:help submatch<\/code><\/li>\n<li><code>:help printf<\/code><\/li>\n<li><code>:help \/magic<\/code><\/li>\n<li><code>:help :s<\/code><\/li>\n<\/ul>\n"},{"title":"Google Account Signup forces you to submit phone number if you use Firefox","link":"https:\/\/cedaei.com\/posts\/google-signup-firefox\/","pubDate":"Tue, 12 May 2020 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/google-signup-firefox\/","description":"<p>A few weeks ago, I needed to create a Google Account since my college teachers have decided to use Google Classroom (yes, they are violating Google&rsquo;s <a href=\"https:\/\/support.google.com\/edu\/classroom\/answer\/6025224\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>TOS<\/a>\n by not using a Google Education Suite.). I have ditched all Google Services quite a long time ago and made my previous account dormant (There are a few YouTube videos hosted with it so I didn&rsquo;t delete it). Thus, I decided to create a new account for the sake of Google Classroom. Started the standard procedure, went to Google, clicked Sign In, clicked Create Account.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/google1.webp\" alt=\"Sign Up Page\"><\/p>\n<p>Pretty standard so far. I fill in the details and this is the next page I arrive at.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/google2.webp\" alt=\"Verify your phone number\"><\/p>\n<p>Okay, I can&rsquo;t skip this apparently. I am pretty privacy paranoid and I definitely don&rsquo;t want to give my phone number to Google. So, I started thinking why would they need my phone number. The first obvious thought is that they require it for anti-spam. The next thought was if they require it for everybody. I take a screenshot and start ranting about it in a group of friends. One of them tries making an account and this does not show up for him. Weird, I thought. Turns out he was using Google Chrome. Instead after filling in the name and basic details, he is prompted with this page instead.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/google3.webp\" alt=\"Enter details\"><\/p>\n<p>I thought, that this might be because of the privacy addons I am using. So, I created a fresh firefox profile with no addons, no history, nothing. Just a stock Firefox browser. Same, still asks me for phone number to verify. I asked the same friend who did not get a request asking for phone number while using Google Chrome to try out via Firefox. Same results for him. I tried Chrome and I wasn&rsquo;t asked for phone number either.<\/p>\n<p>Here is the list of all tried combinations.<\/p>\n<table>\n<thead>\n<tr>\n<th>Operating System<\/th>\n<th>Browser<\/th>\n<th>Location<\/th>\n<th>Asked for Phone Number<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Linux<\/td>\n<td>Firefox<\/td>\n<td>India<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Windows<\/td>\n<td>Firefox<\/td>\n<td>India<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Windows<\/td>\n<td>Firefox<\/td>\n<td>Europe<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td>Linux<\/td>\n<td>Chrome<\/td>\n<td>India<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>Windows<\/td>\n<td>Chrome<\/td>\n<td>India<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td>Windows<\/td>\n<td>Chrome<\/td>\n<td>Europe<\/td>\n<td>No<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>So, to sum up, if we were using Firefox, we were forced to enter phone number. If we were using Google Chrome, we were not.<\/p>\n"},{"title":"React Native with Gitlab CI\/CD","link":"https:\/\/cedaei.com\/posts\/react-native-with-gitlab-ci\/","pubDate":"Mon, 06 Apr 2020 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/react-native-with-gitlab-ci\/","description":"<h2 id=\"introduction\">Introduction<\/h2>\n<p>The aim of setting up CI\/CD is to automatically build the app on every commit\nand send the APK. I looked into Gitlab CI\/CD for this purpose since\n<a href=\"https:\/\/gitlab.com\/ceda_ei\/sonzai\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Sonzai<\/a>\n is already hosted on Gitlab.<\/p>\n<h2 id=\"build-stage\">Build Stage<\/h2>\n<p>To get started, I needed to choose a docker image in which the repo would be\nbuilt. After looking around a bit, I found that the\n<a href=\"https:\/\/github.com\/react-native-community\/\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>react-native-community<\/a>\n has an\nofficial <a href=\"https:\/\/hub.docker.com\/r\/reactnativecommunity\/react-native-android\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Docker\nimage<\/a>\n, the\nsource of which can be found on\n<a href=\"https:\/\/github.com\/react-native-community\/docker-android\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>GitHub<\/a>\n.<\/p>\n<p>Gitlab CI\/CD is controlled by a versioned file in the repo: <code>.gitlab-ci.yml<\/code>.\nTo start off, I added the <code>build<\/code> job in the file and set image to the docker\nimage above.<\/p>\n<pre><code class=\"language-yml\">stages:\n    - build\n\nbuild:\n    image: reactnativecommunity\/react-native-android\n    stage: build\n<\/code><\/pre>\n<p>I needed to add the commands to be executed in order to build the app. Those\ncommands are added under the <code>script<\/code> key as an array. In my case, I need to\nrun <code>yarn install<\/code> to install all dependencies followed by <code>.\/gradlew assembleRelease<\/code> in the <code>android<\/code> directory. The file now looks like.<\/p>\n<pre><code class=\"language-yml\">stages:\n    - build\n\nbuild:\n    image: reactnativecommunity\/react-native-android\n    stage: build\n    script:\n        - yarn install\n        - cd android &amp;&amp; chmod +x gradlew\n        - .\/gradlew assembleRelease\n<\/code><\/pre>\n<p>Next, once the build is done, I need to export the <code>outputs<\/code> generated in the\nbuild to be consumed by the deploy stage. This is done by adding an <code>artifacts<\/code>\nobject which contains a <code>path<\/code> array for all the paths that need to be included\nin the artifacts. In this case, the outputs are in\n<code>android\/app\/build\/outputs\/<\/code>. The file now looks like:<\/p>\n<pre><code class=\"language-yml\">stages:\n    - build\n    - deploy\n\nbuild:\n    image: reactnativecommunity\/react-native-android\n    stage: build\n    script:\n        - yarn install\n        - cd android &amp;&amp; chmod +x gradlew\n        - .\/gradlew assembleRelease\n    artifacts:\n        paths:\n            - android\/app\/build\/outputs\/\n<\/code><\/pre>\n<p>The build stage is now done.<\/p>\n<h2 id=\"deploy-stage\">Deploy Stage<\/h2>\n<p>Although I could use the same image, the image is fairly large and takes time\nto initialize. So, I used\n<a href=\"https:\/\/hub.docker.com\/r\/curlimages\/curl\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>curlimages\/curl<\/a>\n which is an alpine\nimage with curl added and thus is really light. I will be using <code>curl<\/code> to\nupload the file to Telegram. Check out the\n<a href=\"core.telegram.org\/bots\/api\" \n  \n  \n>documentation<\/a>\n for bots API. Adding the deploy\nstage, the file looks as:<\/p>\n<pre><code class=\"language-yml\">stages:\n    - build\n    - deploy\n\nbuild:\n    image: reactnativecommunity\/react-native-android\n    stage: build\n    script:\n        - yarn install\n        - cd android &amp;&amp; chmod +x gradlew\n        - .\/gradlew assembleRelease\n    artifacts:\n        paths:\n            - android\/app\/build\/outputs\/\n\n\ndeploy_tg:\n    image: curlimages\/curl\n    stage: deploy\n<\/code><\/pre>\n<p>I created a bot via <a href=\"https:\/\/t.me\/BotFather\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>@BotFather<\/a>\n and added it to a\n<a href=\"https:\/\/t.me\/sonzai_builds\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>channel<\/a>\n. Next, I got the channel&rsquo;s chat ID. I\nstored the Bot Token and the channel&rsquo;s chat ID as variables in Gitlab&rsquo;s UI\nunder Repository &gt; Settings &gt; CI \/ CD &gt; Variables as <code>TG_BOT_TOKEN<\/code> and\n<code>TG_CHAT_ID<\/code> respectively.<\/p>\n<p><img src=\"https:\/\/cedaei.com\/images\/gitlab-ci.jpg\" alt=\"Gitlab&rsquo;s UI screenshot\"><\/p>\n<p>Next, I added a curl request in the script array to make the actual request to\nTelegram Bot API which utilizes these variables. It also utilizes some\npredefined variables in <a href=\"https:\/\/gitlab.com\/help\/ci\/variables\/predefined_variables.md\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Gitlab&rsquo;s default\nenvironment<\/a>\n.\nHere is the final <code>.gitlab-ci.yml<\/code><\/p>\n<pre><code class=\"language-yml\">stages:\n    - build\n    - deploy\n\nbuild:\n    image: reactnativecommunity\/react-native-android\n    stage: build\n    script:\n        - yarn install\n        - cd android &amp;&amp; chmod +x gradlew\n        - .\/gradlew assembleRelease\n    artifacts:\n        paths:\n            - android\/app\/build\/outputs\/\n\n\ndeploy_tg:\n    image: curlimages\/curl\n    stage: deploy\n    script:\n        - &gt;-\n            curl\n            -F chat_id=$TG_CHAT_ID\n            -F document=@android\/app\/build\/outputs\/apk\/release\/app-release.apk\n            -F caption=&quot; &lt;b&gt;Branch&lt;\/b&gt;: &lt;code&gt;$CI_COMMIT_BRANCH&lt;\/code&gt;\n\n            &lt;b&gt;Commit&lt;\/b&gt;: &lt;code&gt;$CI_COMMIT_SHORT_SHA&lt;\/code&gt;\n\n            &lt;b&gt;Tag(if any)&lt;\/b&gt;: &lt;code&gt;$CI_COMMIT_TAG&lt;\/code&gt;\n\n\n            &lt;code&gt;$CI_COMMIT_MESSAGE&lt;\/code&gt;&quot;\n            -F parse_mode=html\n            https:\/\/api.telegram.org\/bot${TG_BOT_TOKEN}\/sendDocument\n<\/code><\/pre>\n<p>Here is the first build using this.<\/p>\n\n\n<script async src=\"https:\/\/telegram.org\/js\/telegram-widget.js?7\" data-telegram-post=\"sonzai_builds\/8\" data-width=\"100%\"><\/script>\n\n\n"},{"title":"Sorting VCF files with Vim folds","link":"https:\/\/cedaei.com\/posts\/vim-folds\/","pubDate":"Tue, 23 Jul 2019 00:00:00 +0000","guid":"https:\/\/cedaei.com\/posts\/vim-folds\/","description":"<h2 id=\"getting-started\">Getting Started<\/h2>\n<p>I initiated a repository for the plugin.<\/p>\n<pre><code class=\"language-sh\">cd ~\/repos\/\ngit init vcf.Vim\n<\/code><\/pre>\n<p>I use <a href=\"https:\/\/github.com\/junegunn\/Vim-plug\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Vim-plug<\/a>\n for package management\nin Vim and added that to <code>Vim-plug<\/code><\/p>\n<pre><code class=\"language-Vim\">Plug '~\/repos\/vcf.Vim\/'\n<\/code><\/pre>\n<p>Next, I exported all my contacts into a Virtual Contact File.<\/p>\n<h2 id=\"filetype-detection\">Filetype Detection<\/h2>\n<p>To get started I needed Vim to assign a filetype to VCFs. A small <code>autocmd<\/code> is\nenough to take care of that. By convention, filetype detection files go in\n<code>ftdetect<\/code> directory.<\/p>\n<p><code>ftdetect\/vcf.Vim<\/code><\/p>\n<pre><code class=\"language-Vim\">au BufNewFile,BufRead *.vcf set filetype=vcf\n<\/code><\/pre>\n<p>This sets the filetype of all files with filenames ending with <code>.vcf<\/code> to <code>vcf<\/code><\/p>\n<h2 id=\"folding\">Folding<\/h2>\n<p>Plugins for specific filetypes are stored in the <code>ftplugin<\/code> directory. Vim\nsources the file if the filetype matches the filename without the <code>.vim<\/code>\nextension e.g. if the filetype is set to <code>javascript<\/code>, <code>javascript.vim<\/code> in\n<code>ftplugin<\/code> directory will be sourced.<\/p>\n<p>Each contact in a VCF looks like this:<\/p>\n<pre><code class=\"language-vcf\">BEGIN:VCARD\nVERSION:2.1\nN:Lname;Fname;;;\nFN:Fname Lname\nTEL;VOICE:+911234567890\nEND:VCARD\n<\/code><\/pre>\n<p>I want my folds to look like<\/p>\n<pre><code class=\"language-vcf\">Fname Lname\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\u00b7\n<\/code><\/pre>\n<p>I am going to use <code>expr<\/code> foldmethod. This can simply be done using <code>set foldmethod=expr<\/code>. What this tells vim is to run a function on every line and\nset the fold level based on that. To set the expression to run, we use <code>set foldexpr=OurFunction()<\/code>.<\/p>\n<p>Let&rsquo;s write a function first and set <code>foldexpr<\/code> to it. Define the function by\nthe usual syntax.<\/p>\n<pre><code class=\"language-vim\">function! VCFFold()\nendfunction\n\nset foldmethod=expr\nset foldexpr=VCFFold()\n<\/code><\/pre>\n<p>When the function is run, vim sets a special variable <code>v:num<\/code> that tells us\nwhich line the function is being run on. To get the current line, we use the\n<code>getline<\/code> function.<\/p>\n<pre><code class=\"language-vim\">let thisline = getline(v:lnum)\n<\/code><\/pre>\n<p>We want to start a new level fold at every line which starts with <code>BEGIN<\/code>. For\nall the other lines, we want to keep the same fold level as previous line since\nwe don&rsquo;t have nested folds in VCF. Vim folds with expr work by running the\nfunction on each line and determining the fold level of that line based on the\nreturn value of that function. Some of the return values are:<\/p>\n<ul>\n<li><code>&gt;n<\/code> - This tells vim to start a new <code>n<\/code>th level fold there<\/li>\n<li><code>=<\/code> - This tells vim that the fold level is same as previous line.<\/li>\n<li><code>n<\/code> - This tells vim that the fold level is <code>n<\/code><\/li>\n<\/ul>\n<p>There are more return values. Check <code>:help fold-expr<\/code><\/p>\n<p>We can simply set the fold level to <code>&gt;1<\/code> at every <code>BEGIN<\/code> and set it to <code>=<\/code> on\nevery other line. This way, every contact will end up in a fold starting at the\n<code>BEGIN<\/code> of every contact. We can simply use <code>match<\/code> function to check if the\nline begins with <code>BEGIN<\/code> and return <code>&gt;1<\/code> in that case, else we will return <code>=<\/code>.<\/p>\n<pre><code class=\"language-vim\">if match(thisline, '^BEGIN') &gt;= 0\n\treturn &quot;&gt;1&quot;\nendif\nreturn &quot;=&quot;\n<\/code><\/pre>\n<p>Putting it all together, we get.<\/p>\n<pre><code class=\"language-vim\">function! VCFFold()\n\tlet thisline = getline(v:lnum)\n\tif match(thisline, '^BEGIN') &gt;= 0\n\t\treturn &quot;&gt;1&quot;\n\tendif\n\treturn &quot;=&quot;\nendfunction\nset foldmethod=expr\nset foldexpr=VCFFold()\n<\/code><\/pre>\n<h2 id=\"fold-text\">Fold Text<\/h2>\n<p>To set the fold text, we have to set the <code>foldtext<\/code> to a function. Let&rsquo;s create\na funtion named <code>VCFFoldText<\/code> for this purpose and set <code>foldtext<\/code> to it.<\/p>\n<pre><code class=\"language-vim\">function! VCFFoldText()\nendfunction\n\nset foldtext=VCFFoldText()\n<\/code><\/pre>\n<p>The name is stored as a line <code>N:Lname;Fname;;;<\/code>. Two special variables set for\nfoldtext function are <code>v:foldstart<\/code> and <code>v:foldend<\/code> which are the line numbers\nwhere the current fold starts and ends. We can iterate from <code>v:foldstart<\/code> to\n<code>v:foldend<\/code> using the range function. While iterating, we can simply look for a\nline that begins with <code>N:<\/code> and return the name from it. If we don&rsquo;t find any\nsuch line, we can return <code>No Name<\/code>.<\/p>\n<pre><code class=\"language-vim\">function! VCFFoldText()\n\tfor i in range(v:foldstart, v:foldend)\n\t\tlet l:thisline = getline(i)\n\t\tif match(l:thisline, '^N:') &gt;= 0\n\t\t\t&quot; Return the string here\n\t\tendif\n\tendfor\n\treturn &quot;No Name&quot;\nendfunction\n<\/code><\/pre>\n<p>All we need to do is split the parts on semi-colons and join that array back\ndepending on our preferences of whether we want first name first or last name\nfirst. In my case I want first name first.<\/p>\n<pre><code class=\"language-vim\">let l:parts = split(l:thisline, ';')\nreturn substitute(join(l:parts[1:], &quot; &quot;) . l:parts[0][2:], '\\s\\+', ' ', 'g')\n<\/code><\/pre>\n<p><code>split<\/code> splits the string into an array with the second parameter (<code>;<\/code> in this\ncase) as delimiter. I then join all the elements from first element (skipping\nthe zeroth element which is the last name) and then append the zeroth element\nwithout the first two characters (since those are <code>N:<\/code>). Finally, I replace\nmultiple spaces with one space.<\/p>\n<h2 id=\"complete-program\">Complete Program<\/h2>\n<pre><code class=\"language-vim\">function! VCFFold()\n\tlet thisline = getline(v:lnum)\n\tif match(thisline, '^BEGIN') &gt;= 0\n\t\treturn &quot;&gt;1&quot;\n\tendif\n\treturn &quot;=&quot;\nendfunction\nset foldmethod=expr\nset foldexpr=VCFFold()\n\nfunction! VCFFoldText()\n\tfor i in range(v:foldstart, v:foldend)\n\t\tlet l:thisline = getline(i)\n\t\tif match(l:thisline, '^N:') &gt;= 0\n\t\t\tlet l:parts = split(l:thisline, ';')\n\t\t\treturn substitute(join(l:parts[1:], &quot; &quot;) . l:parts[0][2:], '\\s\\+', ' ', 'g')\n\t\tendif\n\tendfor\n\treturn &quot;No Name&quot;\nendfunction\n\nset foldtext=VCFFoldText()\n<\/code><\/pre>\n<h2 id=\"installation\">Installation<\/h2>\n<p>If you just want the above program, it is available as\n<a href=\"https:\/\/gitlab.com\/ceda_ei\/vcf.vim\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>vcf.vim<\/a>\n. You can install it with\n<a href=\"https:\/\/github.com\/junegunn\/Vim-plug\" \n  \n   target=\"_blank\" rel=\"noreferrer noopener\" \n>Vim-plug<\/a>\n via:<\/p>\n<pre><code class=\"language-vim\">Plug 'https:\/\/gitlab.com\/ceda_ei\/vcf.vim.git'\n<\/code><\/pre>\n"}]}}